From 183a9d70886de9c1701f62dddbaabafa2748951d Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 22 Jun 2022 15:03:19 -0700 Subject: [PATCH] update controller image and some dependencies --- .../Dockerfile.builder | 24 +- .../Dockerfile.run_base | 13 +- ext/central-controller-docker/main.sh | 6 +- ext/hiredis-1.0.2/.gitignore | 9 + ext/hiredis-1.0.2/.travis.yml | 131 + ext/hiredis-1.0.2/CHANGELOG.md | 364 + ext/hiredis-1.0.2/CMakeLists.txt | 165 + ext/hiredis-1.0.2/COPYING | 29 + ext/hiredis-1.0.2/Makefile | 308 + ext/hiredis-1.0.2/README.md | 664 + ext/hiredis-1.0.2/adapters/ae.h | 130 + ext/hiredis-1.0.2/adapters/glib.h | 156 + ext/hiredis-1.0.2/adapters/ivykis.h | 84 + ext/hiredis-1.0.2/adapters/libev.h | 179 + ext/hiredis-1.0.2/adapters/libevent.h | 175 + ext/hiredis-1.0.2/adapters/libuv.h | 117 + ext/hiredis-1.0.2/adapters/macosx.h | 115 + ext/hiredis-1.0.2/adapters/qt.h | 135 + ext/hiredis-1.0.2/alloc.c | 86 + ext/hiredis-1.0.2/alloc.h | 91 + ext/hiredis-1.0.2/appveyor.yml | 24 + ext/hiredis-1.0.2/async.c | 887 + ext/hiredis-1.0.2/async.h | 147 + ext/hiredis-1.0.2/async_private.h | 75 + ext/hiredis-1.0.2/dict.c | 352 + ext/hiredis-1.0.2/dict.h | 126 + ext/hiredis-1.0.2/examples/CMakeLists.txt | 49 + ext/hiredis-1.0.2/examples/example-ae.c | 62 + ext/hiredis-1.0.2/examples/example-glib.c | 73 + ext/hiredis-1.0.2/examples/example-ivykis.c | 60 + ext/hiredis-1.0.2/examples/example-libev.c | 54 + .../examples/example-libevent-ssl.c | 90 + ext/hiredis-1.0.2/examples/example-libevent.c | 67 + ext/hiredis-1.0.2/examples/example-libuv.c | 56 + ext/hiredis-1.0.2/examples/example-macosx.c | 66 + ext/hiredis-1.0.2/examples/example-push.c | 160 + ext/hiredis-1.0.2/examples/example-qt.cpp | 46 + ext/hiredis-1.0.2/examples/example-qt.h | 32 + ext/hiredis-1.0.2/examples/example-ssl.c | 110 + ext/hiredis-1.0.2/examples/example.c | 91 + ext/hiredis-1.0.2/fmacros.h | 12 + ext/hiredis-1.0.2/hiredis-config.cmake.in | 13 + ext/hiredis-1.0.2/hiredis.c | 1174 + ext/hiredis-1.0.2/hiredis.h | 336 + ext/hiredis-1.0.2/hiredis.pc.in | 12 + ext/hiredis-1.0.2/hiredis_ssl-config.cmake.in | 13 + ext/hiredis-1.0.2/hiredis_ssl.h | 127 + ext/hiredis-1.0.2/hiredis_ssl.pc.in | 12 + ext/hiredis-1.0.2/include/hiredis/alloc.h | 91 + ext/hiredis-1.0.2/include/hiredis/async.h | 147 + .../include/hiredis/async_private.h | 75 + ext/hiredis-1.0.2/include/hiredis/dict.h | 126 + ext/hiredis-1.0.2/include/hiredis/fmacros.h | 12 + ext/hiredis-1.0.2/include/hiredis/hiredis.h | 336 + .../include/hiredis/hiredis_ssl.h | 127 + ext/hiredis-1.0.2/include/hiredis/net.h | 56 + ext/hiredis-1.0.2/include/hiredis/read.h | 129 + ext/hiredis-1.0.2/include/hiredis/sds.h | 278 + ext/hiredis-1.0.2/include/hiredis/sdsalloc.h | 44 + .../include/hiredis/sockcompat.h | 92 + ext/hiredis-1.0.2/include/hiredis/win32.h | 56 + .../lib/ubuntu22.04/libhiredis.a | Bin 0 -> 557016 bytes ext/hiredis-1.0.2/net.c | 612 + ext/hiredis-1.0.2/net.h | 56 + ext/hiredis-1.0.2/read.c | 739 + ext/hiredis-1.0.2/read.h | 129 + ext/hiredis-1.0.2/sds.c | 1289 + ext/hiredis-1.0.2/sds.h | 278 + ext/hiredis-1.0.2/sdsalloc.h | 44 + ext/hiredis-1.0.2/sockcompat.c | 248 + ext/hiredis-1.0.2/sockcompat.h | 92 + ext/hiredis-1.0.2/ssl.c | 526 + ext/hiredis-1.0.2/test.c | 1401 ++ ext/hiredis-1.0.2/test.sh | 78 + ext/hiredis-1.0.2/win32.h | 56 + ext/libpqxx-7.7.3/.circleci/config.yml | 60 + ext/libpqxx-7.7.3/.clang-format | 71 + ext/libpqxx-7.7.3/.cmake-format | 184 + ext/libpqxx-7.7.3/.github/workflows/stale.yml | 19 + ext/libpqxx-7.7.3/.gitignore | 49 + ext/libpqxx-7.7.3/.lgtm.yml | 9 + ext/libpqxx-7.7.3/.lift/ignoreFiles | 3 + ext/libpqxx-7.7.3/AUTHORS | 4 + ext/libpqxx-7.7.3/BUILDING-cmake.md | 272 + ext/libpqxx-7.7.3/BUILDING-configure.md | 275 + ext/libpqxx-7.7.3/CMakeLists.txt | 66 + ext/libpqxx-7.7.3/COPYING | 27 + ext/libpqxx-7.7.3/INSTALL | 2 + ext/libpqxx-7.7.3/Makefile.am | 23 + ext/libpqxx-7.7.3/Makefile.in | 1253 + ext/libpqxx-7.7.3/NEWS | 1040 + ext/libpqxx-7.7.3/README.md | 199 + ext/libpqxx-7.7.3/VERSION | 1 + ext/libpqxx-7.7.3/aclocal.m4 | 1187 + ext/libpqxx-7.7.3/appveyor.yml | 30 + ext/libpqxx-7.7.3/autogen.sh | 44 + ext/libpqxx-7.7.3/cmake/config.cmake | 157 + ext/libpqxx-7.7.3/cmake/libpqxx-config.cmake | 4 + ext/libpqxx-7.7.3/compile_flags.in | 1 + ext/libpqxx-7.7.3/config-tests/README.md | 22 + .../config-tests/charconv_float.cxx | 16 + .../config-tests/charconv_int.cxx | 16 + ext/libpqxx-7.7.3/config-tests/cmp.cxx | 8 + ext/libpqxx-7.7.3/config-tests/concepts.cxx | 21 + .../config-tests/cxa_demangle.cxx | 19 + ext/libpqxx-7.7.3/config-tests/fs.cxx | 9 + ext/libpqxx-7.7.3/config-tests/gcc_pure.cxx | 10 + .../config-tests/gcc_visibility.cxx | 12 + ext/libpqxx-7.7.3/config-tests/likely.cxx | 15 + .../config-tests/multidim-subscript.cxx | 14 + ext/libpqxx-7.7.3/config-tests/need_fslib.cxx | 21 + ext/libpqxx-7.7.3/config-tests/poll.cxx | 7 + ext/libpqxx-7.7.3/config-tests/sleep_for.cxx | 28 + ext/libpqxx-7.7.3/config-tests/span.cxx | 8 + ext/libpqxx-7.7.3/config-tests/strerror_r.cxx | 14 + ext/libpqxx-7.7.3/config-tests/strerror_s.cxx | 11 + .../config-tests/thread_local.cxx | 15 + .../config-tests/year_month_day.cxx | 7 + ext/libpqxx-7.7.3/config/Makefile.am | 8 + ext/libpqxx-7.7.3/config/Makefile.in | 470 + ext/libpqxx-7.7.3/config/compile | 348 + ext/libpqxx-7.7.3/config/config.guess | 1502 ++ ext/libpqxx-7.7.3/config/config.sub | 1714 ++ ext/libpqxx-7.7.3/config/depcomp | 791 + ext/libpqxx-7.7.3/config/install-sh | 520 + ext/libpqxx-7.7.3/config/ltmain.sh | 11251 +++++++++ ext/libpqxx-7.7.3/config/m4/Makefile.am | 3 + ext/libpqxx-7.7.3/config/m4/libtool.m4 | 8394 +++++++ ext/libpqxx-7.7.3/config/m4/ltoptions.m4 | 437 + ext/libpqxx-7.7.3/config/m4/ltsugar.m4 | 124 + ext/libpqxx-7.7.3/config/m4/ltversion.m4 | 23 + ext/libpqxx-7.7.3/config/m4/lt~obsolete.m4 | 99 + ext/libpqxx-7.7.3/config/missing | 215 + ext/libpqxx-7.7.3/config/mkinstalldirs | 40 + ext/libpqxx-7.7.3/config/test-driver | 148 + ext/libpqxx-7.7.3/configitems | 26 + ext/libpqxx-7.7.3/configure | 20443 ++++++++++++++++ ext/libpqxx-7.7.3/configure.ac | 738 + ext/libpqxx-7.7.3/doc/CMakeLists.txt | 50 + ext/libpqxx-7.7.3/doc/Doxyfile.in | 1280 + ext/libpqxx-7.7.3/doc/Makefile.am | 51 + ext/libpqxx-7.7.3/doc/Makefile.in | 507 + ext/libpqxx-7.7.3/doc/conf.py | 199 + ext/libpqxx-7.7.3/doc/index.rst | 20 + ext/libpqxx-7.7.3/include/CMakeLists.txt | 68 + .../include/CMakeLists.txt.template | 23 + ext/libpqxx-7.7.3/include/Makefile.am | 79 + ext/libpqxx-7.7.3/include/Makefile.in | 802 + ext/libpqxx-7.7.3/include/pqxx/Makefile.am | 13 + ext/libpqxx-7.7.3/include/pqxx/Makefile.in | 556 + ext/libpqxx-7.7.3/include/pqxx/array | 6 + ext/libpqxx-7.7.3/include/pqxx/array.hxx | 103 + ext/libpqxx-7.7.3/include/pqxx/binarystring | 6 + .../include/pqxx/binarystring.hxx | 236 + ext/libpqxx-7.7.3/include/pqxx/blob | 6 + ext/libpqxx-7.7.3/include/pqxx/blob.hxx | 351 + ext/libpqxx-7.7.3/include/pqxx/composite | 6 + ext/libpqxx-7.7.3/include/pqxx/composite.hxx | 149 + ext/libpqxx-7.7.3/include/pqxx/config.h.in | 121 + ext/libpqxx-7.7.3/include/pqxx/connection | 8 + ext/libpqxx-7.7.3/include/pqxx/connection.hxx | 1261 + ext/libpqxx-7.7.3/include/pqxx/cursor | 8 + ext/libpqxx-7.7.3/include/pqxx/cursor.hxx | 483 + ext/libpqxx-7.7.3/include/pqxx/dbtransaction | 8 + .../include/pqxx/dbtransaction.hxx | 70 + .../include/pqxx/doc/accessing-results.md | 157 + .../include/pqxx/doc/binary-data.md | 56 + .../include/pqxx/doc/datatypes.md | 373 + .../include/pqxx/doc/escaping.md | 74 + .../include/pqxx/doc/getting-started.md | 142 + .../include/pqxx/doc/mainpage.md | 28 + .../include/pqxx/doc/mainpage.md.template | 28 + .../include/pqxx/doc/parameters.md | 90 + .../include/pqxx/doc/performance.md | 24 + .../include/pqxx/doc/prepared-statement.md | 125 + ext/libpqxx-7.7.3/include/pqxx/doc/streams.md | 107 + .../include/pqxx/doc/thread-safety.md | 29 + ext/libpqxx-7.7.3/include/pqxx/errorhandler | 8 + .../include/pqxx/errorhandler.hxx | 92 + ext/libpqxx-7.7.3/include/pqxx/except | 8 + ext/libpqxx-7.7.3/include/pqxx/except.hxx | 447 + ext/libpqxx-7.7.3/include/pqxx/field | 8 + ext/libpqxx-7.7.3/include/pqxx/field.hxx | 542 + .../include/pqxx/internal/array-composite.hxx | 305 + .../include/pqxx/internal/callgate.hxx | 70 + .../include/pqxx/internal/concat.hxx | 45 + .../include/pqxx/internal/conversions.hxx | 1188 + .../include/pqxx/internal/encoding_group.hxx | 60 + .../include/pqxx/internal/encodings.hxx | 90 + .../gates/connection-errorhandler.hxx | 26 + .../internal/gates/connection-largeobject.hxx | 35 + .../connection-notification_receiver.hxx | 29 + .../internal/gates/connection-pipeline.hxx | 23 + .../internal/gates/connection-sql_cursor.hxx | 19 + .../internal/gates/connection-stream_from.hxx | 15 + .../internal/gates/connection-stream_to.hxx | 17 + .../internal/gates/connection-transaction.hxx | 44 + .../gates/errorhandler-connection.hxx | 13 + .../gates/icursor_iterator-icursorstream.hxx | 24 + .../gates/icursorstream-icursor_iterator.hxx | 32 + .../pqxx/internal/gates/result-connection.hxx | 14 + .../pqxx/internal/gates/result-creation.hxx | 24 + .../pqxx/internal/gates/result-pipeline.hxx | 16 + .../pqxx/internal/gates/result-sql_cursor.hxx | 13 + .../internal/gates/transaction-sql_cursor.hxx | 10 + .../gates/transaction-transaction_focus.hxx | 30 + .../include/pqxx/internal/header-post.hxx | 22 + .../include/pqxx/internal/header-pre.hxx | 169 + .../pqxx/internal/ignore-deprecated-post.hxx | 15 + .../pqxx/internal/ignore-deprecated-pre.hxx | 28 + .../include/pqxx/internal/libpq-forward.hxx | 31 + .../include/pqxx/internal/result_iter.hxx | 124 + .../include/pqxx/internal/result_iterator.hxx | 389 + .../include/pqxx/internal/sql_cursor.hxx | 118 + .../pqxx/internal/statement_parameters.hxx | 131 + .../include/pqxx/internal/stream_iterator.hxx | 105 + .../include/pqxx/internal/wait.hxx | 18 + ext/libpqxx-7.7.3/include/pqxx/isolation | 8 + ext/libpqxx-7.7.3/include/pqxx/isolation.hxx | 75 + ext/libpqxx-7.7.3/include/pqxx/largeobject | 8 + .../include/pqxx/largeobject.hxx | 735 + ext/libpqxx-7.7.3/include/pqxx/nontransaction | 8 + .../include/pqxx/nontransaction.hxx | 76 + ext/libpqxx-7.7.3/include/pqxx/notification | 8 + .../include/pqxx/notification.hxx | 94 + ext/libpqxx-7.7.3/include/pqxx/params | 8 + ext/libpqxx-7.7.3/include/pqxx/params.hxx | 383 + ext/libpqxx-7.7.3/include/pqxx/pipeline | 8 + ext/libpqxx-7.7.3/include/pqxx/pipeline.hxx | 237 + ext/libpqxx-7.7.3/include/pqxx/pqxx | 28 + .../include/pqxx/prepared_statement | 3 + .../include/pqxx/prepared_statement.hxx | 3 + ext/libpqxx-7.7.3/include/pqxx/range | 6 + ext/libpqxx-7.7.3/include/pqxx/range.hxx | 515 + ext/libpqxx-7.7.3/include/pqxx/result | 16 + ext/libpqxx-7.7.3/include/pqxx/result.hxx | 335 + .../include/pqxx/robusttransaction | 8 + .../include/pqxx/robusttransaction.hxx | 120 + ext/libpqxx-7.7.3/include/pqxx/row | 11 + ext/libpqxx-7.7.3/include/pqxx/row.hxx | 561 + ext/libpqxx-7.7.3/include/pqxx/separated_list | 6 + .../include/pqxx/separated_list.hxx | 142 + ext/libpqxx-7.7.3/include/pqxx/strconv | 6 + ext/libpqxx-7.7.3/include/pqxx/strconv.hxx | 468 + ext/libpqxx-7.7.3/include/pqxx/stream_from | 8 + .../include/pqxx/stream_from.hxx | 361 + ext/libpqxx-7.7.3/include/pqxx/stream_to | 8 + ext/libpqxx-7.7.3/include/pqxx/stream_to.hxx | 455 + ext/libpqxx-7.7.3/include/pqxx/subtransaction | 8 + .../include/pqxx/subtransaction.hxx | 96 + ext/libpqxx-7.7.3/include/pqxx/time | 6 + ext/libpqxx-7.7.3/include/pqxx/time.hxx | 88 + ext/libpqxx-7.7.3/include/pqxx/transaction | 8 + .../include/pqxx/transaction.hxx | 108 + .../include/pqxx/transaction_base | 9 + .../include/pqxx/transaction_base.hxx | 810 + .../include/pqxx/transaction_focus | 7 + .../include/pqxx/transaction_focus.hxx | 89 + ext/libpqxx-7.7.3/include/pqxx/transactor | 8 + ext/libpqxx-7.7.3/include/pqxx/transactor.hxx | 147 + ext/libpqxx-7.7.3/include/pqxx/types | 7 + ext/libpqxx-7.7.3/include/pqxx/types.hxx | 173 + ext/libpqxx-7.7.3/include/pqxx/util | 6 + ext/libpqxx-7.7.3/include/pqxx/util.hxx | 521 + ext/libpqxx-7.7.3/include/pqxx/version | 7 + ext/libpqxx-7.7.3/include/pqxx/version.hxx | 55 + .../include/pqxx/version.hxx.template | 55 + ext/libpqxx-7.7.3/include/pqxx/zview | 6 + ext/libpqxx-7.7.3/include/pqxx/zview.hxx | 163 + .../install/ubuntu22.04/include/pqxx/array | 6 + .../ubuntu22.04/include/pqxx/array.hxx | 103 + .../ubuntu22.04/include/pqxx/binarystring | 6 + .../ubuntu22.04/include/pqxx/binarystring.hxx | 236 + .../install/ubuntu22.04/include/pqxx/blob | 6 + .../install/ubuntu22.04/include/pqxx/blob.hxx | 351 + .../ubuntu22.04/include/pqxx/composite | 6 + .../ubuntu22.04/include/pqxx/composite.hxx | 149 + .../include/pqxx/config-public-compiler.h | 81 + .../ubuntu22.04/include/pqxx/connection | 8 + .../ubuntu22.04/include/pqxx/connection.hxx | 1261 + .../install/ubuntu22.04/include/pqxx/cursor | 8 + .../ubuntu22.04/include/pqxx/cursor.hxx | 483 + .../ubuntu22.04/include/pqxx/dbtransaction | 8 + .../include/pqxx/dbtransaction.hxx | 70 + .../ubuntu22.04/include/pqxx/errorhandler | 8 + .../ubuntu22.04/include/pqxx/errorhandler.hxx | 92 + .../install/ubuntu22.04/include/pqxx/except | 8 + .../ubuntu22.04/include/pqxx/except.hxx | 447 + .../install/ubuntu22.04/include/pqxx/field | 8 + .../ubuntu22.04/include/pqxx/field.hxx | 542 + .../include/pqxx/internal/array-composite.hxx | 305 + .../include/pqxx/internal/callgate.hxx | 70 + .../include/pqxx/internal/concat.hxx | 45 + .../include/pqxx/internal/conversions.hxx | 1188 + .../include/pqxx/internal/encoding_group.hxx | 60 + .../include/pqxx/internal/encodings.hxx | 90 + .../gates/connection-errorhandler.hxx | 26 + .../internal/gates/connection-largeobject.hxx | 35 + .../connection-notification_receiver.hxx | 29 + .../internal/gates/connection-pipeline.hxx | 23 + .../internal/gates/connection-sql_cursor.hxx | 19 + .../internal/gates/connection-stream_from.hxx | 15 + .../internal/gates/connection-stream_to.hxx | 17 + .../internal/gates/connection-transaction.hxx | 44 + .../gates/errorhandler-connection.hxx | 13 + .../gates/icursor_iterator-icursorstream.hxx | 24 + .../gates/icursorstream-icursor_iterator.hxx | 32 + .../pqxx/internal/gates/result-connection.hxx | 14 + .../pqxx/internal/gates/result-creation.hxx | 24 + .../pqxx/internal/gates/result-pipeline.hxx | 16 + .../pqxx/internal/gates/result-sql_cursor.hxx | 13 + .../internal/gates/transaction-sql_cursor.hxx | 10 + .../gates/transaction-transaction_focus.hxx | 30 + .../include/pqxx/internal/header-post.hxx | 22 + .../include/pqxx/internal/header-pre.hxx | 169 + .../pqxx/internal/ignore-deprecated-post.hxx | 15 + .../pqxx/internal/ignore-deprecated-pre.hxx | 28 + .../include/pqxx/internal/libpq-forward.hxx | 31 + .../include/pqxx/internal/result_iter.hxx | 124 + .../include/pqxx/internal/result_iterator.hxx | 389 + .../include/pqxx/internal/sql_cursor.hxx | 118 + .../pqxx/internal/statement_parameters.hxx | 131 + .../include/pqxx/internal/stream_iterator.hxx | 105 + .../include/pqxx/internal/wait.hxx | 18 + .../ubuntu22.04/include/pqxx/isolation | 8 + .../ubuntu22.04/include/pqxx/isolation.hxx | 75 + .../ubuntu22.04/include/pqxx/largeobject | 8 + .../ubuntu22.04/include/pqxx/largeobject.hxx | 735 + .../ubuntu22.04/include/pqxx/nontransaction | 8 + .../include/pqxx/nontransaction.hxx | 76 + .../ubuntu22.04/include/pqxx/notification | 8 + .../ubuntu22.04/include/pqxx/notification.hxx | 94 + .../install/ubuntu22.04/include/pqxx/params | 8 + .../ubuntu22.04/include/pqxx/params.hxx | 383 + .../install/ubuntu22.04/include/pqxx/pipeline | 8 + .../ubuntu22.04/include/pqxx/pipeline.hxx | 237 + .../install/ubuntu22.04/include/pqxx/pqxx | 28 + .../include/pqxx/prepared_statement | 3 + .../include/pqxx/prepared_statement.hxx | 3 + .../install/ubuntu22.04/include/pqxx/range | 6 + .../ubuntu22.04/include/pqxx/range.hxx | 515 + .../install/ubuntu22.04/include/pqxx/result | 16 + .../ubuntu22.04/include/pqxx/result.hxx | 335 + .../include/pqxx/robusttransaction | 8 + .../include/pqxx/robusttransaction.hxx | 120 + .../install/ubuntu22.04/include/pqxx/row | 11 + .../install/ubuntu22.04/include/pqxx/row.hxx | 561 + .../ubuntu22.04/include/pqxx/separated_list | 6 + .../include/pqxx/separated_list.hxx | 142 + .../install/ubuntu22.04/include/pqxx/strconv | 6 + .../ubuntu22.04/include/pqxx/strconv.hxx | 468 + .../ubuntu22.04/include/pqxx/stream_from | 8 + .../ubuntu22.04/include/pqxx/stream_from.hxx | 361 + .../ubuntu22.04/include/pqxx/stream_to | 8 + .../ubuntu22.04/include/pqxx/stream_to.hxx | 455 + .../ubuntu22.04/include/pqxx/subtransaction | 8 + .../include/pqxx/subtransaction.hxx | 96 + .../install/ubuntu22.04/include/pqxx/time | 6 + .../install/ubuntu22.04/include/pqxx/time.hxx | 88 + .../ubuntu22.04/include/pqxx/transaction | 8 + .../ubuntu22.04/include/pqxx/transaction.hxx | 108 + .../ubuntu22.04/include/pqxx/transaction_base | 9 + .../include/pqxx/transaction_base.hxx | 810 + .../include/pqxx/transaction_focus | 7 + .../include/pqxx/transaction_focus.hxx | 89 + .../ubuntu22.04/include/pqxx/transactor | 8 + .../ubuntu22.04/include/pqxx/transactor.hxx | 147 + .../install/ubuntu22.04/include/pqxx/types | 7 + .../ubuntu22.04/include/pqxx/types.hxx | 173 + .../install/ubuntu22.04/include/pqxx/util | 6 + .../install/ubuntu22.04/include/pqxx/util.hxx | 521 + .../install/ubuntu22.04/include/pqxx/version | 7 + .../ubuntu22.04/include/pqxx/version.hxx | 55 + .../install/ubuntu22.04/include/pqxx/zview | 6 + .../ubuntu22.04/include/pqxx/zview.hxx | 163 + .../libpqxx/libpqxx-config-version.cmake | 70 + .../lib/cmake/libpqxx/libpqxx-config.cmake | 4 + .../libpqxx/libpqxx-targets-noconfig.cmake | 19 + .../lib/cmake/libpqxx/libpqxx-targets.cmake | 99 + .../install/ubuntu22.04/lib/libpqxx-7.7.a | Bin 0 -> 4600578 bytes .../install/ubuntu22.04/lib/libpqxx.a | 1 + .../ubuntu22.04/lib/pkgconfig/libpqxx.pc | 10 + .../share/doc/libpqxx/accessing-results.md | 157 + .../share/doc/libpqxx/binary-data.md | 56 + .../share/doc/libpqxx/datatypes.md | 373 + .../ubuntu22.04/share/doc/libpqxx/escaping.md | 74 + .../share/doc/libpqxx/getting-started.md | 142 + .../ubuntu22.04/share/doc/libpqxx/mainpage.md | 28 + .../share/doc/libpqxx/parameters.md | 90 + .../share/doc/libpqxx/performance.md | 24 + .../share/doc/libpqxx/prepared-statement.md | 125 + .../ubuntu22.04/share/doc/libpqxx/streams.md | 107 + .../share/doc/libpqxx/thread-safety.md | 29 + ext/libpqxx-7.7.3/libpqxx.pc.in | 10 + ext/libpqxx-7.7.3/requirements.json | 9 + ext/libpqxx-7.7.3/src/CMakeLists.txt | 91 + ext/libpqxx-7.7.3/src/Makefile.am | 44 + ext/libpqxx-7.7.3/src/Makefile.in | 809 + ext/libpqxx-7.7.3/src/array.cxx | 240 + ext/libpqxx-7.7.3/src/binarystring.cxx | 108 + ext/libpqxx-7.7.3/src/blob.cxx | 337 + ext/libpqxx-7.7.3/src/connection.cxx | 1274 + ext/libpqxx-7.7.3/src/cursor.cxx | 339 + ext/libpqxx-7.7.3/src/encodings.cxx | 839 + ext/libpqxx-7.7.3/src/errorhandler.cxx | 43 + ext/libpqxx-7.7.3/src/except.cxx | 126 + ext/libpqxx-7.7.3/src/field.cxx | 80 + ext/libpqxx-7.7.3/src/largeobject.cxx | 322 + ext/libpqxx-7.7.3/src/notification.cxx | 35 + ext/libpqxx-7.7.3/src/params.cxx | 122 + ext/libpqxx-7.7.3/src/pipeline.cxx | 448 + ext/libpqxx-7.7.3/src/pqxx-source.hxx | 30 + ext/libpqxx-7.7.3/src/result.cxx | 536 + ext/libpqxx-7.7.3/src/robusttransaction.cxx | 221 + ext/libpqxx-7.7.3/src/row.cxx | 250 + ext/libpqxx-7.7.3/src/sql_cursor.cxx | 276 + ext/libpqxx-7.7.3/src/strconv.cxx | 785 + ext/libpqxx-7.7.3/src/stream_from.cxx | 327 + ext/libpqxx-7.7.3/src/stream_to.cxx | 170 + ext/libpqxx-7.7.3/src/subtransaction.cxx | 68 + ext/libpqxx-7.7.3/src/time.cxx | 226 + ext/libpqxx-7.7.3/src/transaction.cxx | 107 + ext/libpqxx-7.7.3/src/transaction_base.cxx | 539 + ext/libpqxx-7.7.3/src/util.cxx | 194 + ext/libpqxx-7.7.3/src/version.cxx | 27 + ext/libpqxx-7.7.3/src/wait.cxx | 136 + ext/libpqxx-7.7.3/test/CMakeLists.txt | 24 + ext/libpqxx-7.7.3/test/Makefile.am | 129 + ext/libpqxx-7.7.3/test/Makefile.am.template | 38 + ext/libpqxx-7.7.3/test/Makefile.in | 1302 + ext/libpqxx-7.7.3/test/runner.cxx | 203 + ext/libpqxx-7.7.3/test/test00.cxx | 114 + ext/libpqxx-7.7.3/test/test01.cxx | 33 + ext/libpqxx-7.7.3/test/test02.cxx | 76 + ext/libpqxx-7.7.3/test/test04.cxx | 77 + ext/libpqxx-7.7.3/test/test07.cxx | 133 + ext/libpqxx-7.7.3/test/test10.cxx | 106 + ext/libpqxx-7.7.3/test/test11.cxx | 76 + ext/libpqxx-7.7.3/test/test13.cxx | 86 + ext/libpqxx-7.7.3/test/test14.cxx | 36 + ext/libpqxx-7.7.3/test/test16.cxx | 46 + ext/libpqxx-7.7.3/test/test17.cxx | 29 + ext/libpqxx-7.7.3/test/test18.cxx | 80 + ext/libpqxx-7.7.3/test/test20.cxx | 95 + ext/libpqxx-7.7.3/test/test21.cxx | 70 + ext/libpqxx-7.7.3/test/test26.cxx | 86 + ext/libpqxx-7.7.3/test/test29.cxx | 108 + ext/libpqxx-7.7.3/test/test30.cxx | 73 + ext/libpqxx-7.7.3/test/test32.cxx | 78 + ext/libpqxx-7.7.3/test/test37.cxx | 78 + ext/libpqxx-7.7.3/test/test39.cxx | 89 + ext/libpqxx-7.7.3/test/test46.cxx | 74 + ext/libpqxx-7.7.3/test/test56.cxx | 24 + ext/libpqxx-7.7.3/test/test60.cxx | 85 + ext/libpqxx-7.7.3/test/test61.cxx | 61 + ext/libpqxx-7.7.3/test/test62.cxx | 61 + ext/libpqxx-7.7.3/test/test69.cxx | 55 + ext/libpqxx-7.7.3/test/test70.cxx | 101 + ext/libpqxx-7.7.3/test/test71.cxx | 74 + ext/libpqxx-7.7.3/test/test72.cxx | 53 + ext/libpqxx-7.7.3/test/test74.cxx | 72 + ext/libpqxx-7.7.3/test/test75.cxx | 108 + ext/libpqxx-7.7.3/test/test76.cxx | 52 + ext/libpqxx-7.7.3/test/test77.cxx | 28 + ext/libpqxx-7.7.3/test/test78.cxx | 69 + ext/libpqxx-7.7.3/test/test79.cxx | 70 + ext/libpqxx-7.7.3/test/test82.cxx | 154 + ext/libpqxx-7.7.3/test/test84.cxx | 109 + ext/libpqxx-7.7.3/test/test87.cxx | 83 + ext/libpqxx-7.7.3/test/test88.cxx | 91 + ext/libpqxx-7.7.3/test/test89.cxx | 44 + ext/libpqxx-7.7.3/test/test90.cxx | 23 + ext/libpqxx-7.7.3/test/test_helpers.hxx | 305 + ext/libpqxx-7.7.3/test/test_types.hxx | 242 + ext/libpqxx-7.7.3/test/unit/CMakeLists.txt | 25 + ext/libpqxx-7.7.3/test/unit/test_array.cxx | 548 + .../test/unit/test_binarystring.cxx | 211 + ext/libpqxx-7.7.3/test/unit/test_blob.cxx | 644 + .../test/unit/test_cancel_query.cxx | 25 + ext/libpqxx-7.7.3/test/unit/test_column.cxx | 61 + .../test/unit/test_composite.cxx | 98 + .../test/unit/test_connection.cxx | 212 + ext/libpqxx-7.7.3/test/unit/test_cursor.cxx | 50 + .../test/unit/test_encodings.cxx | 114 + .../test/unit/test_error_verbosity.cxx | 37 + .../test/unit/test_errorhandler.cxx | 223 + ext/libpqxx-7.7.3/test/unit/test_escape.cxx | 228 + .../test/unit/test_exceptions.cxx | 45 + ext/libpqxx-7.7.3/test/unit/test_field.cxx | 57 + ext/libpqxx-7.7.3/test/unit/test_float.cxx | 175 + .../test/unit/test_largeobject.cxx | 58 + .../test/unit/test_nonblocking_connect.cxx | 27 + .../test/unit/test_notification.cxx | 86 + ext/libpqxx-7.7.3/test/unit/test_pipeline.cxx | 64 + .../test/unit/test_prepared_statement.cxx | 334 + ext/libpqxx-7.7.3/test/unit/test_range.cxx | 555 + .../test/unit/test_read_transaction.cxx | 22 + .../test/unit/test_result_iteration.cxx | 137 + .../test/unit/test_result_slicing.cxx | 157 + ext/libpqxx-7.7.3/test/unit/test_row.cxx | 84 + .../test/unit/test_separated_list.cxx | 33 + .../unit/test_simultaneous_transactions.cxx | 20 + .../test/unit/test_sql_cursor.cxx | 270 + .../test/unit/test_stateless_cursor.cxx | 85 + ext/libpqxx-7.7.3/test/unit/test_strconv.cxx | 143 + .../test/unit/test_stream_from.cxx | 344 + .../test/unit/test_stream_to.cxx | 445 + .../test/unit/test_string_conversion.cxx | 178 + .../test/unit/test_subtransaction.cxx | 78 + .../test/unit/test_test_helpers.cxx | 214 + .../test/unit/test_thread_safety_model.cxx | 23 + ext/libpqxx-7.7.3/test/unit/test_time.cxx | 86 + .../test/unit/test_transaction.cxx | 113 + .../test/unit/test_transaction_base.cxx | 106 + .../test/unit/test_transaction_focus.cxx | 53 + .../test/unit/test_transactor.cxx | 130 + .../test/unit/test_type_name.cxx | 19 + ext/libpqxx-7.7.3/test/unit/test_zview.cxx | 16 + ext/libpqxx-7.7.3/tools/Makefile.am | 20 + ext/libpqxx-7.7.3/tools/Makefile.in | 638 + ext/libpqxx-7.7.3/tools/deprecations | 6 + ext/libpqxx-7.7.3/tools/extract_version | 73 + ext/libpqxx-7.7.3/tools/format | 20 + ext/libpqxx-7.7.3/tools/lint | 197 + ext/libpqxx-7.7.3/tools/m4esc.py | 70 + ext/libpqxx-7.7.3/tools/pqxxthreadsafety.cxx | 10 + ext/libpqxx-7.7.3/tools/rmlo.cxx | 39 + ext/libpqxx-7.7.3/tools/splitconfig | 244 + ext/libpqxx-7.7.3/tools/template2mak.py | 194 + ext/libpqxx-7.7.3/tools/test_all.py | 630 + ext/libpqxx-7.7.3/tools/todo | 31 + ext/libpqxx-7.7.3/tools/update-copyright | 29 + .../.github/ISSUE_TEMPLATE/bug_report.md | 26 + .../.github/ISSUE_TEMPLATE/feature_request.md | 20 + .../.github/ISSUE_TEMPLATE/question.md | 27 + ext/redis-plus-plus-1.3.3/.gitignore | 32 + ext/redis-plus-plus-1.3.3/.travis.yml | 32 + ext/redis-plus-plus-1.3.3/CMakeLists.txt | 307 + ext/redis-plus-plus-1.3.3/LICENSE | 201 + ext/redis-plus-plus-1.3.3/README.md | 2604 ++ .../cmake/redis++-config.cmake.in | 12 + ext/redis-plus-plus-1.3.3/cmake/redis++.pc.in | 12 + .../include/sw/redis++/cmd_formatter.h | 775 + .../ubuntu22.04/include/sw/redis++/command.h | 2271 ++ .../include/sw/redis++/command_args.h | 180 + .../include/sw/redis++/command_options.h | 211 + .../include/sw/redis++/connection.h | 237 + .../include/sw/redis++/connection_pool.h | 182 + .../include/sw/redis++/cxx_utils.h | 46 + .../ubuntu22.04/include/sw/redis++/errors.h | 166 + .../ubuntu22.04/include/sw/redis++/pipeline.h | 49 + .../include/sw/redis++/queued_redis.h | 2013 ++ .../include/sw/redis++/queued_redis.hpp | 275 + .../ubuntu22.04/include/sw/redis++/redis++.h | 25 + .../ubuntu22.04/include/sw/redis++/redis.h | 3631 +++ .../ubuntu22.04/include/sw/redis++/redis.hpp | 1342 + .../include/sw/redis++/redis_cluster.h | 1439 ++ .../include/sw/redis++/redis_cluster.hpp | 1403 ++ .../ubuntu22.04/include/sw/redis++/reply.h | 435 + .../ubuntu22.04/include/sw/redis++/sentinel.h | 141 + .../ubuntu22.04/include/sw/redis++/shards.h | 115 + .../include/sw/redis++/shards_pool.h | 121 + .../include/sw/redis++/subscriber.h | 231 + .../ubuntu22.04/include/sw/redis++/tls.h | 47 + .../include/sw/redis++/transaction.h | 77 + .../ubuntu22.04/include/sw/redis++/utils.h | 193 + .../install/ubuntu22.04/lib/libredis++.a | Bin 0 -> 1399538 bytes .../ubuntu22.04/lib/pkgconfig/redis++.pc | 12 + .../redis++/redis++-config-version.cmake | 48 + .../share/cmake/redis++/redis++-config.cmake | 36 + .../redis++/redis++-targets-release.cmake | 19 + .../share/cmake/redis++/redis++-targets.cmake | 94 + .../src/sw/redis++/async_connection.cpp | 448 + .../src/sw/redis++/async_connection.h | 498 + .../src/sw/redis++/async_connection_pool.cpp | 347 + .../src/sw/redis++/async_connection_pool.h | 181 + .../src/sw/redis++/async_redis++.h | 23 + .../src/sw/redis++/async_redis.cpp | 52 + .../src/sw/redis++/async_redis.h | 789 + .../src/sw/redis++/async_redis_cluster.cpp | 37 + .../src/sw/redis++/async_redis_cluster.h | 819 + .../src/sw/redis++/async_sentinel.cpp | 110 + .../src/sw/redis++/async_sentinel.h | 89 + .../src/sw/redis++/async_shards_pool.cpp | 374 + .../src/sw/redis++/async_shards_pool.h | 127 + .../src/sw/redis++/cmd_formatter.h | 775 + .../src/sw/redis++/command.cpp | 376 + .../src/sw/redis++/command.h | 2271 ++ .../src/sw/redis++/command_args.h | 180 + .../src/sw/redis++/command_options.cpp | 201 + .../src/sw/redis++/command_options.h | 211 + .../src/sw/redis++/connection.cpp | 494 + .../src/sw/redis++/connection.h | 237 + .../src/sw/redis++/connection_pool.cpp | 276 + .../src/sw/redis++/connection_pool.h | 182 + .../src/sw/redis++/crc16.cpp | 96 + .../src/sw/redis++/cxx11/cxx_utils.h | 113 + .../src/sw/redis++/cxx17/cxx_utils.h | 46 + .../src/sw/redis++/errors.cpp | 142 + .../src/sw/redis++/errors.h | 166 + .../src/sw/redis++/event_loop.cpp | 273 + .../src/sw/redis++/event_loop.h | 119 + .../src/sw/redis++/future/boost/async_utils.h | 39 + .../src/sw/redis++/future/std/async_utils.h | 36 + .../src/sw/redis++/no_tls/tls.h | 47 + .../src/sw/redis++/pipeline.cpp | 35 + .../src/sw/redis++/pipeline.h | 49 + .../src/sw/redis++/queued_redis.h | 2013 ++ .../src/sw/redis++/queued_redis.hpp | 275 + .../src/sw/redis++/redis++.h | 25 + .../src/sw/redis++/redis.cpp | 929 + .../src/sw/redis++/redis.h | 3631 +++ .../src/sw/redis++/redis.hpp | 1342 + .../src/sw/redis++/redis_cluster.cpp | 802 + .../src/sw/redis++/redis_cluster.h | 1439 ++ .../src/sw/redis++/redis_cluster.hpp | 1403 ++ .../src/sw/redis++/reply.cpp | 158 + .../src/sw/redis++/reply.h | 435 + .../src/sw/redis++/sentinel.cpp | 362 + .../src/sw/redis++/sentinel.h | 141 + .../src/sw/redis++/shards.cpp | 52 + .../src/sw/redis++/shards.h | 115 + .../src/sw/redis++/shards_pool.cpp | 369 + .../src/sw/redis++/shards_pool.h | 121 + .../src/sw/redis++/subscriber.cpp | 222 + .../src/sw/redis++/subscriber.h | 231 + .../src/sw/redis++/tls/tls.cpp | 72 + .../src/sw/redis++/tls/tls.h | 77 + .../src/sw/redis++/transaction.cpp | 123 + .../src/sw/redis++/transaction.h | 77 + .../src/sw/redis++/utils.h | 193 + ext/redis-plus-plus-1.3.3/test/CMakeLists.txt | 58 + .../test/src/sw/redis++/benchmark_test.h | 83 + .../test/src/sw/redis++/benchmark_test.hpp | 178 + .../src/sw/redis++/connection_cmds_test.h | 49 + .../src/sw/redis++/connection_cmds_test.hpp | 50 + .../test/src/sw/redis++/geo_cmds_test.h | 47 + .../test/src/sw/redis++/geo_cmds_test.hpp | 187 + .../test/src/sw/redis++/hash_cmds_test.h | 55 + .../test/src/sw/redis++/hash_cmds_test.hpp | 183 + .../src/sw/redis++/hyperloglog_cmds_test.h | 47 + .../src/sw/redis++/hyperloglog_cmds_test.hpp | 67 + .../test/src/sw/redis++/keys_cmds_test.h | 55 + .../test/src/sw/redis++/keys_cmds_test.hpp | 166 + .../test/src/sw/redis++/list_cmds_test.h | 55 + .../test/src/sw/redis++/list_cmds_test.hpp | 154 + .../sw/redis++/pipeline_transaction_test.h | 61 + .../sw/redis++/pipeline_transaction_test.hpp | 293 + .../test/src/sw/redis++/pubsub_test.h | 53 + .../test/src/sw/redis++/pubsub_test.hpp | 244 + .../test/src/sw/redis++/sanity_test.h | 80 + .../test/src/sw/redis++/sanity_test.hpp | 481 + .../test/src/sw/redis++/script_cmds_test.h | 49 + .../test/src/sw/redis++/script_cmds_test.hpp | 126 + .../test/src/sw/redis++/set_cmds_test.h | 53 + .../test/src/sw/redis++/set_cmds_test.hpp | 184 + .../test/src/sw/redis++/stream_cmds_test.h | 54 + .../test/src/sw/redis++/stream_cmds_test.hpp | 255 + .../test/src/sw/redis++/string_cmds_test.h | 57 + .../test/src/sw/redis++/string_cmds_test.hpp | 247 + .../test/src/sw/redis++/test_main.cpp | 433 + .../test/src/sw/redis++/threads_test.h | 51 + .../test/src/sw/redis++/threads_test.hpp | 147 + .../test/src/sw/redis++/utils.h | 105 + .../test/src/sw/redis++/zset_cmds_test.h | 61 + .../test/src/sw/redis++/zset_cmds_test.hpp | 422 + make-linux.mk | 4 +- 667 files changed, 173907 insertions(+), 17 deletions(-) create mode 100644 ext/hiredis-1.0.2/.gitignore create mode 100644 ext/hiredis-1.0.2/.travis.yml create mode 100644 ext/hiredis-1.0.2/CHANGELOG.md create mode 100644 ext/hiredis-1.0.2/CMakeLists.txt create mode 100644 ext/hiredis-1.0.2/COPYING create mode 100644 ext/hiredis-1.0.2/Makefile create mode 100644 ext/hiredis-1.0.2/README.md create mode 100644 ext/hiredis-1.0.2/adapters/ae.h create mode 100644 ext/hiredis-1.0.2/adapters/glib.h create mode 100644 ext/hiredis-1.0.2/adapters/ivykis.h create mode 100644 ext/hiredis-1.0.2/adapters/libev.h create mode 100644 ext/hiredis-1.0.2/adapters/libevent.h create mode 100644 ext/hiredis-1.0.2/adapters/libuv.h create mode 100644 ext/hiredis-1.0.2/adapters/macosx.h create mode 100644 ext/hiredis-1.0.2/adapters/qt.h create mode 100644 ext/hiredis-1.0.2/alloc.c create mode 100644 ext/hiredis-1.0.2/alloc.h create mode 100644 ext/hiredis-1.0.2/appveyor.yml create mode 100644 ext/hiredis-1.0.2/async.c create mode 100644 ext/hiredis-1.0.2/async.h create mode 100644 ext/hiredis-1.0.2/async_private.h create mode 100644 ext/hiredis-1.0.2/dict.c create mode 100644 ext/hiredis-1.0.2/dict.h create mode 100644 ext/hiredis-1.0.2/examples/CMakeLists.txt create mode 100644 ext/hiredis-1.0.2/examples/example-ae.c create mode 100644 ext/hiredis-1.0.2/examples/example-glib.c create mode 100644 ext/hiredis-1.0.2/examples/example-ivykis.c create mode 100644 ext/hiredis-1.0.2/examples/example-libev.c create mode 100644 ext/hiredis-1.0.2/examples/example-libevent-ssl.c create mode 100644 ext/hiredis-1.0.2/examples/example-libevent.c create mode 100644 ext/hiredis-1.0.2/examples/example-libuv.c create mode 100644 ext/hiredis-1.0.2/examples/example-macosx.c create mode 100644 ext/hiredis-1.0.2/examples/example-push.c create mode 100644 ext/hiredis-1.0.2/examples/example-qt.cpp create mode 100644 ext/hiredis-1.0.2/examples/example-qt.h create mode 100644 ext/hiredis-1.0.2/examples/example-ssl.c create mode 100644 ext/hiredis-1.0.2/examples/example.c create mode 100644 ext/hiredis-1.0.2/fmacros.h create mode 100644 ext/hiredis-1.0.2/hiredis-config.cmake.in create mode 100644 ext/hiredis-1.0.2/hiredis.c create mode 100644 ext/hiredis-1.0.2/hiredis.h create mode 100644 ext/hiredis-1.0.2/hiredis.pc.in create mode 100644 ext/hiredis-1.0.2/hiredis_ssl-config.cmake.in create mode 100644 ext/hiredis-1.0.2/hiredis_ssl.h create mode 100644 ext/hiredis-1.0.2/hiredis_ssl.pc.in create mode 100644 ext/hiredis-1.0.2/include/hiredis/alloc.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/async.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/async_private.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/dict.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/fmacros.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/hiredis.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/hiredis_ssl.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/net.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/read.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/sds.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/sdsalloc.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/sockcompat.h create mode 100644 ext/hiredis-1.0.2/include/hiredis/win32.h create mode 100644 ext/hiredis-1.0.2/lib/ubuntu22.04/libhiredis.a create mode 100644 ext/hiredis-1.0.2/net.c create mode 100644 ext/hiredis-1.0.2/net.h create mode 100644 ext/hiredis-1.0.2/read.c create mode 100644 ext/hiredis-1.0.2/read.h create mode 100644 ext/hiredis-1.0.2/sds.c create mode 100644 ext/hiredis-1.0.2/sds.h create mode 100644 ext/hiredis-1.0.2/sdsalloc.h create mode 100644 ext/hiredis-1.0.2/sockcompat.c create mode 100644 ext/hiredis-1.0.2/sockcompat.h create mode 100644 ext/hiredis-1.0.2/ssl.c create mode 100644 ext/hiredis-1.0.2/test.c create mode 100755 ext/hiredis-1.0.2/test.sh create mode 100644 ext/hiredis-1.0.2/win32.h create mode 100644 ext/libpqxx-7.7.3/.circleci/config.yml create mode 100644 ext/libpqxx-7.7.3/.clang-format create mode 100644 ext/libpqxx-7.7.3/.cmake-format create mode 100644 ext/libpqxx-7.7.3/.github/workflows/stale.yml create mode 100644 ext/libpqxx-7.7.3/.gitignore create mode 100644 ext/libpqxx-7.7.3/.lgtm.yml create mode 100644 ext/libpqxx-7.7.3/.lift/ignoreFiles create mode 100644 ext/libpqxx-7.7.3/AUTHORS create mode 100644 ext/libpqxx-7.7.3/BUILDING-cmake.md create mode 100644 ext/libpqxx-7.7.3/BUILDING-configure.md create mode 100644 ext/libpqxx-7.7.3/CMakeLists.txt create mode 100644 ext/libpqxx-7.7.3/COPYING create mode 100644 ext/libpqxx-7.7.3/INSTALL create mode 100644 ext/libpqxx-7.7.3/Makefile.am create mode 100644 ext/libpqxx-7.7.3/Makefile.in create mode 100644 ext/libpqxx-7.7.3/NEWS create mode 100644 ext/libpqxx-7.7.3/README.md create mode 100644 ext/libpqxx-7.7.3/VERSION create mode 100644 ext/libpqxx-7.7.3/aclocal.m4 create mode 100644 ext/libpqxx-7.7.3/appveyor.yml create mode 100755 ext/libpqxx-7.7.3/autogen.sh create mode 100644 ext/libpqxx-7.7.3/cmake/config.cmake create mode 100644 ext/libpqxx-7.7.3/cmake/libpqxx-config.cmake create mode 100644 ext/libpqxx-7.7.3/compile_flags.in create mode 100644 ext/libpqxx-7.7.3/config-tests/README.md create mode 100644 ext/libpqxx-7.7.3/config-tests/charconv_float.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/charconv_int.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/cmp.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/concepts.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/cxa_demangle.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/fs.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/gcc_pure.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/gcc_visibility.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/likely.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/multidim-subscript.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/need_fslib.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/poll.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/sleep_for.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/span.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/strerror_r.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/strerror_s.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/thread_local.cxx create mode 100644 ext/libpqxx-7.7.3/config-tests/year_month_day.cxx create mode 100644 ext/libpqxx-7.7.3/config/Makefile.am create mode 100644 ext/libpqxx-7.7.3/config/Makefile.in create mode 100755 ext/libpqxx-7.7.3/config/compile create mode 100755 ext/libpqxx-7.7.3/config/config.guess create mode 100755 ext/libpqxx-7.7.3/config/config.sub create mode 100755 ext/libpqxx-7.7.3/config/depcomp create mode 100755 ext/libpqxx-7.7.3/config/install-sh create mode 100755 ext/libpqxx-7.7.3/config/ltmain.sh create mode 100644 ext/libpqxx-7.7.3/config/m4/Makefile.am create mode 100644 ext/libpqxx-7.7.3/config/m4/libtool.m4 create mode 100644 ext/libpqxx-7.7.3/config/m4/ltoptions.m4 create mode 100644 ext/libpqxx-7.7.3/config/m4/ltsugar.m4 create mode 100644 ext/libpqxx-7.7.3/config/m4/ltversion.m4 create mode 100644 ext/libpqxx-7.7.3/config/m4/lt~obsolete.m4 create mode 100755 ext/libpqxx-7.7.3/config/missing create mode 100755 ext/libpqxx-7.7.3/config/mkinstalldirs create mode 100755 ext/libpqxx-7.7.3/config/test-driver create mode 100644 ext/libpqxx-7.7.3/configitems create mode 100755 ext/libpqxx-7.7.3/configure create mode 100644 ext/libpqxx-7.7.3/configure.ac create mode 100644 ext/libpqxx-7.7.3/doc/CMakeLists.txt create mode 100644 ext/libpqxx-7.7.3/doc/Doxyfile.in create mode 100644 ext/libpqxx-7.7.3/doc/Makefile.am create mode 100644 ext/libpqxx-7.7.3/doc/Makefile.in create mode 100644 ext/libpqxx-7.7.3/doc/conf.py create mode 100644 ext/libpqxx-7.7.3/doc/index.rst create mode 100644 ext/libpqxx-7.7.3/include/CMakeLists.txt create mode 100644 ext/libpqxx-7.7.3/include/CMakeLists.txt.template create mode 100644 ext/libpqxx-7.7.3/include/Makefile.am create mode 100644 ext/libpqxx-7.7.3/include/Makefile.in create mode 100644 ext/libpqxx-7.7.3/include/pqxx/Makefile.am create mode 100644 ext/libpqxx-7.7.3/include/pqxx/Makefile.in create mode 100644 ext/libpqxx-7.7.3/include/pqxx/array create mode 100644 ext/libpqxx-7.7.3/include/pqxx/array.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/binarystring create mode 100644 ext/libpqxx-7.7.3/include/pqxx/binarystring.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/blob create mode 100644 ext/libpqxx-7.7.3/include/pqxx/blob.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/composite create mode 100644 ext/libpqxx-7.7.3/include/pqxx/composite.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/config.h.in create mode 100644 ext/libpqxx-7.7.3/include/pqxx/connection create mode 100644 ext/libpqxx-7.7.3/include/pqxx/connection.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/cursor create mode 100644 ext/libpqxx-7.7.3/include/pqxx/cursor.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/dbtransaction create mode 100644 ext/libpqxx-7.7.3/include/pqxx/dbtransaction.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/accessing-results.md create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/binary-data.md create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/datatypes.md create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/escaping.md create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/getting-started.md create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/mainpage.md create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/mainpage.md.template create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/parameters.md create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/performance.md create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/prepared-statement.md create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/streams.md create mode 100644 ext/libpqxx-7.7.3/include/pqxx/doc/thread-safety.md create mode 100644 ext/libpqxx-7.7.3/include/pqxx/errorhandler create mode 100644 ext/libpqxx-7.7.3/include/pqxx/errorhandler.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/except create mode 100644 ext/libpqxx-7.7.3/include/pqxx/except.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/field create mode 100644 ext/libpqxx-7.7.3/include/pqxx/field.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/array-composite.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/callgate.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/concat.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/conversions.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/encoding_group.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/encodings.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-errorhandler.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-largeobject.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-notification_receiver.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-pipeline.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-sql_cursor.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-stream_from.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-stream_to.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-transaction.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/errorhandler-connection.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/icursor_iterator-icursorstream.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/icursorstream-icursor_iterator.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-connection.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-creation.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-pipeline.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-sql_cursor.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/transaction-sql_cursor.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/gates/transaction-transaction_focus.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/header-post.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/header-pre.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/ignore-deprecated-post.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/ignore-deprecated-pre.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/libpq-forward.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/result_iter.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/result_iterator.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/sql_cursor.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/statement_parameters.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/stream_iterator.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/internal/wait.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/isolation create mode 100644 ext/libpqxx-7.7.3/include/pqxx/isolation.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/largeobject create mode 100644 ext/libpqxx-7.7.3/include/pqxx/largeobject.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/nontransaction create mode 100644 ext/libpqxx-7.7.3/include/pqxx/nontransaction.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/notification create mode 100644 ext/libpqxx-7.7.3/include/pqxx/notification.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/params create mode 100644 ext/libpqxx-7.7.3/include/pqxx/params.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/pipeline create mode 100644 ext/libpqxx-7.7.3/include/pqxx/pipeline.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/pqxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/prepared_statement create mode 100644 ext/libpqxx-7.7.3/include/pqxx/prepared_statement.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/range create mode 100644 ext/libpqxx-7.7.3/include/pqxx/range.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/result create mode 100644 ext/libpqxx-7.7.3/include/pqxx/result.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/robusttransaction create mode 100644 ext/libpqxx-7.7.3/include/pqxx/robusttransaction.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/row create mode 100644 ext/libpqxx-7.7.3/include/pqxx/row.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/separated_list create mode 100644 ext/libpqxx-7.7.3/include/pqxx/separated_list.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/strconv create mode 100644 ext/libpqxx-7.7.3/include/pqxx/strconv.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/stream_from create mode 100644 ext/libpqxx-7.7.3/include/pqxx/stream_from.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/stream_to create mode 100644 ext/libpqxx-7.7.3/include/pqxx/stream_to.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/subtransaction create mode 100644 ext/libpqxx-7.7.3/include/pqxx/subtransaction.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/time create mode 100644 ext/libpqxx-7.7.3/include/pqxx/time.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/transaction create mode 100644 ext/libpqxx-7.7.3/include/pqxx/transaction.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/transaction_base create mode 100644 ext/libpqxx-7.7.3/include/pqxx/transaction_base.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/transaction_focus create mode 100644 ext/libpqxx-7.7.3/include/pqxx/transaction_focus.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/transactor create mode 100644 ext/libpqxx-7.7.3/include/pqxx/transactor.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/types create mode 100644 ext/libpqxx-7.7.3/include/pqxx/types.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/util create mode 100644 ext/libpqxx-7.7.3/include/pqxx/util.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/version create mode 100644 ext/libpqxx-7.7.3/include/pqxx/version.hxx create mode 100644 ext/libpqxx-7.7.3/include/pqxx/version.hxx.template create mode 100644 ext/libpqxx-7.7.3/include/pqxx/zview create mode 100644 ext/libpqxx-7.7.3/include/pqxx/zview.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/array create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/array.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/binarystring create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/binarystring.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/blob create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/blob.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/composite create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/composite.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/config-public-compiler.h create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/connection create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/connection.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/cursor create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/cursor.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/dbtransaction create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/dbtransaction.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/errorhandler create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/errorhandler.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/except create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/except.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/field create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/field.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/array-composite.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/callgate.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/concat.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/conversions.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/encoding_group.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/encodings.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-errorhandler.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-largeobject.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-notification_receiver.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-pipeline.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-sql_cursor.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-stream_from.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-stream_to.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-transaction.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/errorhandler-connection.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/icursor_iterator-icursorstream.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/icursorstream-icursor_iterator.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-connection.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-creation.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-pipeline.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-sql_cursor.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/transaction-sql_cursor.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/transaction-transaction_focus.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/header-post.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/header-pre.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/ignore-deprecated-post.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/ignore-deprecated-pre.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/libpq-forward.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/result_iter.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/result_iterator.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/sql_cursor.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/statement_parameters.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/stream_iterator.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/wait.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/isolation create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/isolation.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/largeobject create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/largeobject.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/nontransaction create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/nontransaction.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/notification create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/notification.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/params create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/params.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pipeline create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pipeline.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pqxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/prepared_statement create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/prepared_statement.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/range create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/range.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/result create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/result.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/robusttransaction create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/robusttransaction.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/row create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/row.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/separated_list create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/separated_list.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/strconv create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/strconv.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_from create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_from.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_to create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_to.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/subtransaction create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/subtransaction.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/time create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/time.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_base create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_base.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_focus create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_focus.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transactor create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transactor.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/types create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/types.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/util create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/util.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/version create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/version.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/zview create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/zview.hxx create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-config-version.cmake create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-config.cmake create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-targets-noconfig.cmake create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-targets.cmake create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/lib/libpqxx-7.7.a create mode 120000 ext/libpqxx-7.7.3/install/ubuntu22.04/lib/libpqxx.a create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/lib/pkgconfig/libpqxx.pc create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/accessing-results.md create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/binary-data.md create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/datatypes.md create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/escaping.md create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/getting-started.md create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/mainpage.md create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/parameters.md create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/performance.md create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/prepared-statement.md create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/streams.md create mode 100644 ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/thread-safety.md create mode 100644 ext/libpqxx-7.7.3/libpqxx.pc.in create mode 100644 ext/libpqxx-7.7.3/requirements.json create mode 100644 ext/libpqxx-7.7.3/src/CMakeLists.txt create mode 100644 ext/libpqxx-7.7.3/src/Makefile.am create mode 100644 ext/libpqxx-7.7.3/src/Makefile.in create mode 100644 ext/libpqxx-7.7.3/src/array.cxx create mode 100644 ext/libpqxx-7.7.3/src/binarystring.cxx create mode 100644 ext/libpqxx-7.7.3/src/blob.cxx create mode 100644 ext/libpqxx-7.7.3/src/connection.cxx create mode 100644 ext/libpqxx-7.7.3/src/cursor.cxx create mode 100644 ext/libpqxx-7.7.3/src/encodings.cxx create mode 100644 ext/libpqxx-7.7.3/src/errorhandler.cxx create mode 100644 ext/libpqxx-7.7.3/src/except.cxx create mode 100644 ext/libpqxx-7.7.3/src/field.cxx create mode 100644 ext/libpqxx-7.7.3/src/largeobject.cxx create mode 100644 ext/libpqxx-7.7.3/src/notification.cxx create mode 100644 ext/libpqxx-7.7.3/src/params.cxx create mode 100644 ext/libpqxx-7.7.3/src/pipeline.cxx create mode 100644 ext/libpqxx-7.7.3/src/pqxx-source.hxx create mode 100644 ext/libpqxx-7.7.3/src/result.cxx create mode 100644 ext/libpqxx-7.7.3/src/robusttransaction.cxx create mode 100644 ext/libpqxx-7.7.3/src/row.cxx create mode 100644 ext/libpqxx-7.7.3/src/sql_cursor.cxx create mode 100644 ext/libpqxx-7.7.3/src/strconv.cxx create mode 100644 ext/libpqxx-7.7.3/src/stream_from.cxx create mode 100644 ext/libpqxx-7.7.3/src/stream_to.cxx create mode 100644 ext/libpqxx-7.7.3/src/subtransaction.cxx create mode 100644 ext/libpqxx-7.7.3/src/time.cxx create mode 100644 ext/libpqxx-7.7.3/src/transaction.cxx create mode 100644 ext/libpqxx-7.7.3/src/transaction_base.cxx create mode 100644 ext/libpqxx-7.7.3/src/util.cxx create mode 100644 ext/libpqxx-7.7.3/src/version.cxx create mode 100644 ext/libpqxx-7.7.3/src/wait.cxx create mode 100644 ext/libpqxx-7.7.3/test/CMakeLists.txt create mode 100644 ext/libpqxx-7.7.3/test/Makefile.am create mode 100644 ext/libpqxx-7.7.3/test/Makefile.am.template create mode 100644 ext/libpqxx-7.7.3/test/Makefile.in create mode 100644 ext/libpqxx-7.7.3/test/runner.cxx create mode 100644 ext/libpqxx-7.7.3/test/test00.cxx create mode 100644 ext/libpqxx-7.7.3/test/test01.cxx create mode 100644 ext/libpqxx-7.7.3/test/test02.cxx create mode 100644 ext/libpqxx-7.7.3/test/test04.cxx create mode 100644 ext/libpqxx-7.7.3/test/test07.cxx create mode 100644 ext/libpqxx-7.7.3/test/test10.cxx create mode 100644 ext/libpqxx-7.7.3/test/test11.cxx create mode 100644 ext/libpqxx-7.7.3/test/test13.cxx create mode 100644 ext/libpqxx-7.7.3/test/test14.cxx create mode 100644 ext/libpqxx-7.7.3/test/test16.cxx create mode 100644 ext/libpqxx-7.7.3/test/test17.cxx create mode 100644 ext/libpqxx-7.7.3/test/test18.cxx create mode 100644 ext/libpqxx-7.7.3/test/test20.cxx create mode 100644 ext/libpqxx-7.7.3/test/test21.cxx create mode 100644 ext/libpqxx-7.7.3/test/test26.cxx create mode 100644 ext/libpqxx-7.7.3/test/test29.cxx create mode 100644 ext/libpqxx-7.7.3/test/test30.cxx create mode 100644 ext/libpqxx-7.7.3/test/test32.cxx create mode 100644 ext/libpqxx-7.7.3/test/test37.cxx create mode 100644 ext/libpqxx-7.7.3/test/test39.cxx create mode 100644 ext/libpqxx-7.7.3/test/test46.cxx create mode 100644 ext/libpqxx-7.7.3/test/test56.cxx create mode 100644 ext/libpqxx-7.7.3/test/test60.cxx create mode 100644 ext/libpqxx-7.7.3/test/test61.cxx create mode 100644 ext/libpqxx-7.7.3/test/test62.cxx create mode 100644 ext/libpqxx-7.7.3/test/test69.cxx create mode 100644 ext/libpqxx-7.7.3/test/test70.cxx create mode 100644 ext/libpqxx-7.7.3/test/test71.cxx create mode 100644 ext/libpqxx-7.7.3/test/test72.cxx create mode 100644 ext/libpqxx-7.7.3/test/test74.cxx create mode 100644 ext/libpqxx-7.7.3/test/test75.cxx create mode 100644 ext/libpqxx-7.7.3/test/test76.cxx create mode 100644 ext/libpqxx-7.7.3/test/test77.cxx create mode 100644 ext/libpqxx-7.7.3/test/test78.cxx create mode 100644 ext/libpqxx-7.7.3/test/test79.cxx create mode 100644 ext/libpqxx-7.7.3/test/test82.cxx create mode 100644 ext/libpqxx-7.7.3/test/test84.cxx create mode 100644 ext/libpqxx-7.7.3/test/test87.cxx create mode 100644 ext/libpqxx-7.7.3/test/test88.cxx create mode 100644 ext/libpqxx-7.7.3/test/test89.cxx create mode 100644 ext/libpqxx-7.7.3/test/test90.cxx create mode 100644 ext/libpqxx-7.7.3/test/test_helpers.hxx create mode 100644 ext/libpqxx-7.7.3/test/test_types.hxx create mode 100644 ext/libpqxx-7.7.3/test/unit/CMakeLists.txt create mode 100644 ext/libpqxx-7.7.3/test/unit/test_array.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_binarystring.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_blob.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_cancel_query.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_column.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_composite.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_connection.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_cursor.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_encodings.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_error_verbosity.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_errorhandler.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_escape.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_exceptions.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_field.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_float.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_largeobject.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_nonblocking_connect.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_notification.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_pipeline.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_prepared_statement.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_range.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_read_transaction.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_result_iteration.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_result_slicing.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_row.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_separated_list.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_simultaneous_transactions.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_sql_cursor.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_stateless_cursor.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_strconv.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_stream_from.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_stream_to.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_string_conversion.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_subtransaction.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_test_helpers.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_thread_safety_model.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_time.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_transaction.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_transaction_base.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_transaction_focus.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_transactor.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_type_name.cxx create mode 100644 ext/libpqxx-7.7.3/test/unit/test_zview.cxx create mode 100644 ext/libpqxx-7.7.3/tools/Makefile.am create mode 100644 ext/libpqxx-7.7.3/tools/Makefile.in create mode 100755 ext/libpqxx-7.7.3/tools/deprecations create mode 100755 ext/libpqxx-7.7.3/tools/extract_version create mode 100755 ext/libpqxx-7.7.3/tools/format create mode 100755 ext/libpqxx-7.7.3/tools/lint create mode 100755 ext/libpqxx-7.7.3/tools/m4esc.py create mode 100644 ext/libpqxx-7.7.3/tools/pqxxthreadsafety.cxx create mode 100644 ext/libpqxx-7.7.3/tools/rmlo.cxx create mode 100755 ext/libpqxx-7.7.3/tools/splitconfig create mode 100755 ext/libpqxx-7.7.3/tools/template2mak.py create mode 100755 ext/libpqxx-7.7.3/tools/test_all.py create mode 100755 ext/libpqxx-7.7.3/tools/todo create mode 100755 ext/libpqxx-7.7.3/tools/update-copyright create mode 100644 ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/feature_request.md create mode 100644 ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/question.md create mode 100644 ext/redis-plus-plus-1.3.3/.gitignore create mode 100644 ext/redis-plus-plus-1.3.3/.travis.yml create mode 100644 ext/redis-plus-plus-1.3.3/CMakeLists.txt create mode 100644 ext/redis-plus-plus-1.3.3/LICENSE create mode 100644 ext/redis-plus-plus-1.3.3/README.md create mode 100644 ext/redis-plus-plus-1.3.3/cmake/redis++-config.cmake.in create mode 100644 ext/redis-plus-plus-1.3.3/cmake/redis++.pc.in create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cmd_formatter.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_args.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_options.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection_pool.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cxx_utils.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/errors.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/pipeline.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.hpp create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis++.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.hpp create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.hpp create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/reply.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/sentinel.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards_pool.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/subscriber.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/tls.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/transaction.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/utils.h create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/lib/libredis++.a create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/lib/pkgconfig/redis++.pc create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-config-version.cmake create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-config.cmake create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets-release.cmake create mode 100644 ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets.cmake create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection_pool.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection_pool.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis++.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis_cluster.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis_cluster.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_sentinel.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_sentinel.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_shards_pool.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/async_shards_pool.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/cmd_formatter.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/command.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/command.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/command_args.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/command_options.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/command_options.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/connection.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/connection.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/connection_pool.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/connection_pool.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/crc16.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/cxx11/cxx_utils.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/cxx17/cxx_utils.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/errors.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/errors.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/event_loop.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/event_loop.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/future/boost/async_utils.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/future/std/async_utils.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/no_tls/tls.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/pipeline.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/pipeline.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/queued_redis.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/queued_redis.hpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/redis++.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.hpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.hpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/reply.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/reply.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/sentinel.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/sentinel.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/shards.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/shards.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/shards_pool.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/shards_pool.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/subscriber.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/subscriber.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/tls/tls.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/tls/tls.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/transaction.cpp create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/transaction.h create mode 100644 ext/redis-plus-plus-1.3.3/src/sw/redis++/utils.h create mode 100644 ext/redis-plus-plus-1.3.3/test/CMakeLists.txt create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/benchmark_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/benchmark_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/connection_cmds_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/connection_cmds_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/geo_cmds_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/geo_cmds_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hash_cmds_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hash_cmds_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hyperloglog_cmds_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hyperloglog_cmds_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/keys_cmds_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/keys_cmds_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/list_cmds_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/list_cmds_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pipeline_transaction_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pipeline_transaction_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pubsub_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pubsub_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/sanity_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/sanity_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/script_cmds_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/script_cmds_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/set_cmds_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/set_cmds_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/stream_cmds_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/stream_cmds_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/string_cmds_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/string_cmds_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/test_main.cpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/threads_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/threads_test.hpp create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/utils.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/zset_cmds_test.h create mode 100644 ext/redis-plus-plus-1.3.3/test/src/sw/redis++/zset_cmds_test.hpp diff --git a/ext/central-controller-docker/Dockerfile.builder b/ext/central-controller-docker/Dockerfile.builder index 5c2787570..a2592bb78 100644 --- a/ext/central-controller-docker/Dockerfile.builder +++ b/ext/central-controller-docker/Dockerfile.builder @@ -1,13 +1,23 @@ # Dockerfile for building ZeroTier Central Controllers -FROM centos:8 as builder +FROM ubuntu:jammy as builder MAINTAINER Adam Ierymekno , Grant Limberg ARG git_branch=master -RUN yum update -y -RUN yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm -RUN dnf -qy module disable postgresql -RUN yum -y install epel-release && yum -y update && yum clean all -RUN yum groupinstall -y "Development Tools" && yum clean all -RUN yum install -y bash cmake postgresql10 postgresql10-devel clang jemalloc jemalloc-devel libpqxx libpqxx-devel openssl-devel && yum clean all +RUN apt update && apt upgrade -y +RUN apt -y install \ + build-essential \ + pkg-config \ + bash \ + clang \ + libjemalloc2 \ + libjemalloc-dev \ + libpq5 \ + libpq-dev \ + openssl \ + libssl-dev \ + postgresql-client \ + postgresql-client-common \ + curl + RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y diff --git a/ext/central-controller-docker/Dockerfile.run_base b/ext/central-controller-docker/Dockerfile.run_base index 9a08f5f72..e09d214e3 100644 --- a/ext/central-controller-docker/Dockerfile.run_base +++ b/ext/central-controller-docker/Dockerfile.run_base @@ -1,5 +1,8 @@ -FROM centos:8 -RUN yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm -RUN dnf -qy module disable postgresql -RUN yum -y install epel-release && yum -y update && yum clean all -RUN yum install -y jemalloc jemalloc-devel postgresql10 libpqxx libpqxx-devel && yum clean all +FROM ubuntu:jammy +RUN apt update && apt upgrade -y +RUN apt -y install \ + postgresql-client \ + postgresql-client-common \ + libjemalloc2 \ + libpq5 \ + curl diff --git a/ext/central-controller-docker/main.sh b/ext/central-controller-docker/main.sh index 8d505bb94..9db5e4f26 100755 --- a/ext/central-controller-docker/main.sh +++ b/ext/central-controller-docker/main.sh @@ -83,12 +83,12 @@ if [ -n "$DB_SERVER_CA" ]; then echo "secret list" chmod 600 /secrets/db/*.pem ls -l /secrets/db/ - until /usr/pgsql-10/bin/pg_isready -h ${ZT_DB_HOST} -p ${ZT_DB_PORT} -d "sslmode=prefer sslcert=${DB_CLIENT_CERT} sslkey=${DB_CLIENT_KEY} sslrootcert=${DB_SERVER_CA}"; do + until /usr/bin/pg_isready -h ${ZT_DB_HOST} -p ${ZT_DB_PORT} -d "sslmode=prefer sslcert=${DB_CLIENT_CERT} sslkey=${DB_CLIENT_KEY} sslrootcert=${DB_SERVER_CA}"; do echo "Waiting for PostgreSQL..."; sleep 2; done else - until /usr/pgsql-10/bin/pg_isready -h ${ZT_DB_HOST} -p ${ZT_DB_PORT}; do + until /usr/bin/pg_isready -h ${ZT_DB_HOST} -p ${ZT_DB_PORT}; do echo "Waiting for PostgreSQL..."; sleep 2; done @@ -96,5 +96,5 @@ fi export GLIBCXX_FORCE_NEW=1 export GLIBCPP_FORCE_NEW=1 -export LD_PRELOAD="/usr/lib64/libjemalloc.so" +export LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libjemalloc.so.2" exec /usr/local/bin/zerotier-one -p${ZT_CONTROLLER_PORT:-$DEFAULT_PORT} /var/lib/zerotier-one diff --git a/ext/hiredis-1.0.2/.gitignore b/ext/hiredis-1.0.2/.gitignore new file mode 100644 index 000000000..056959ffe --- /dev/null +++ b/ext/hiredis-1.0.2/.gitignore @@ -0,0 +1,9 @@ +/hiredis-test +/examples/hiredis-example* +/*.o +/*.so +/*.dylib +/*.a +/*.pc +*.dSYM +tags diff --git a/ext/hiredis-1.0.2/.travis.yml b/ext/hiredis-1.0.2/.travis.yml new file mode 100644 index 000000000..f9a9460ff --- /dev/null +++ b/ext/hiredis-1.0.2/.travis.yml @@ -0,0 +1,131 @@ +language: c +compiler: + - gcc + - clang + +os: + - linux + - osx + +dist: bionic + +branches: + only: + - staging + - trying + - master + - /^release\/.*$/ + +install: + - if [ "$BITS" == "64" ]; then + wget https://github.com/redis/redis/archive/6.0.6.tar.gz; + tar -xzvf 6.0.6.tar.gz; + pushd redis-6.0.6 && BUILD_TLS=yes make && export PATH=$PWD/src:$PATH && popd; + fi + +before_script: + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then + curl -O https://distfiles.macports.org/MacPorts/MacPorts-2.6.2-10.13-HighSierra.pkg; + sudo installer -pkg MacPorts-2.6.2-10.13-HighSierra.pkg -target /; + export PATH=$PATH:/opt/local/bin && sudo port -v selfupdate; + sudo port -N install openssl redis; + fi; + +addons: + apt: + sources: + - sourceline: 'ppa:chris-lea/redis-server' + packages: + - libc6-dbg + - libc6-dev + - libc6:i386 + - libc6-dev-i386 + - libc6-dbg:i386 + - gcc-multilib + - g++-multilib + - libssl-dev + - libssl-dev:i386 + - valgrind + - redis + +env: + - BITS="32" + - BITS="64" + +script: + - EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON"; + if [ "$BITS" == "64" ]; then + EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DENABLE_SSL_TESTS:BOOL=ON"; + fi; + if [ "$TRAVIS_OS_NAME" == "osx" ]; then + if [ "$BITS" == "32" ]; then + CFLAGS="-m32 -Werror"; + CXXFLAGS="-m32 -Werror"; + LDFLAGS="-m32"; + EXTRA_CMAKE_OPTS=; + else + CFLAGS="-Werror"; + CXXFLAGS="-Werror"; + fi; + else + TEST_PREFIX="valgrind --track-origins=yes --leak-check=full"; + if [ "$BITS" == "32" ]; then + CFLAGS="-m32 -Werror"; + CXXFLAGS="-m32 -Werror"; + LDFLAGS="-m32"; + EXTRA_CMAKE_OPTS=; + else + CFLAGS="-Werror"; + CXXFLAGS="-Werror"; + fi; + fi; + export CFLAGS CXXFLAGS LDFLAGS TEST_PREFIX EXTRA_CMAKE_OPTS + - make && make clean; + if [ "$TRAVIS_OS_NAME" == "osx" ]; then + if [ "$BITS" == "64" ]; then + OPENSSL_PREFIX="$(ls -d /usr/local/Cellar/openssl@1.1/*)" USE_SSL=1 make; + fi; + else + USE_SSL=1 make; + fi; + - mkdir build/ && cd build/ + - cmake .. ${EXTRA_CMAKE_OPTS} + - make VERBOSE=1 + - if [ "$BITS" == "64" ]; then + TEST_SSL=1 SKIPS_AS_FAILS=1 ctest -V; + else + SKIPS_AS_FAILS=1 ctest -V; + fi; + +jobs: + include: + # Windows MinGW cross compile on Linux + - os: linux + dist: xenial + compiler: mingw + addons: + apt: + packages: + - ninja-build + - gcc-mingw-w64-x86-64 + - g++-mingw-w64-x86-64 + script: + - mkdir build && cd build + - CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_WITH_INSTALL_RPATH=on + - ninja -v + + # Windows MSVC 2017 + - os: windows + compiler: msvc + env: + - MATRIX_EVAL="CC=cl.exe && CXX=cl.exe" + before_install: + - eval "${MATRIX_EVAL}" + install: + - choco install ninja + - choco install -y memurai-developer + script: + - mkdir build && cd build + - cmd.exe //C 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat' amd64 '&&' + cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DENABLE_EXAMPLES=ON '&&' ninja -v + - ./hiredis-test.exe diff --git a/ext/hiredis-1.0.2/CHANGELOG.md b/ext/hiredis-1.0.2/CHANGELOG.md new file mode 100644 index 000000000..2a2bc314a --- /dev/null +++ b/ext/hiredis-1.0.2/CHANGELOG.md @@ -0,0 +1,364 @@ +## [1.0.2](https://github.com/redis/hiredis/tree/v1.0.2) - (2021-10-07) + +Announcing Hiredis v1.0.2, which fixes CVE-2021-32765 but returns the SONAME to the correct value of `1.0.0`. + +- [Revert SONAME bump](https://github.com/redis/hiredis/commit/d4e6f109a064690cde64765c654e679fea1d3548) + ([Michael Grunder](https://github.com/michael-grunder)) + +## [1.0.1](https://github.com/redis/hiredis/tree/v1.0.1) - (2021-10-04) + +This release erroneously bumped the SONAME, please use [1.0.2](https://github.com/redis/hiredis/tree/v1.0.2) + +Announcing Hiredis v1.0.1, a security release fixing CVE-2021-32765 + +- Fix for [CVE-2021-32765](https://github.com/redis/hiredis/security/advisories/GHSA-hfm9-39pp-55p2) + [commit](https://github.com/redis/hiredis/commit/76a7b10005c70babee357a7d0f2becf28ec7ed1e) + ([Yossi Gottlieb](https://github.com/yossigo)) + +_Thanks to [Yossi Gottlieb](https://github.com/yossigo) for the security fix and to [Microsoft Security Vulnerability Research](https://www.microsoft.com/en-us/msrc/msvr) for finding the bug._ :sparkling_heart: + +## [1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) - (2020-08-03) + +Announcing Hiredis v1.0.0, which adds support for RESP3, SSL connections, allocator injection, and better Windows support! :tada: + +_A big thanks to everyone who helped with this release. The following list includes everyone who contributed at least five lines, sorted by lines contributed._ :sparkling_heart: + +[Michael Grunder](https://github.com/michael-grunder), [Yossi Gottlieb](https://github.com/yossigo), +[Mark Nunberg](https://github.com/mnunberg), [Marcus Geelnard](https://github.com/mbitsnbites), +[Justin Brewer](https://github.com/justinbrewer), [Valentino Geron](https://github.com/valentinogeron), +[Minun Dragonation](https://github.com/dragonation), [Omri Steiner](https://github.com/OmriSteiner), +[Sangmoon Yi](https://github.com/jman-krafton), [Jinjiazh](https://github.com/jinjiazhang), +[Odin Hultgren Van Der Horst](https://github.com/Miniwoffer), [Muhammad Zahalqa](https://github.com/tryfinally), +[Nick Rivera](https://github.com/heronr), [Qi Yang](https://github.com/movebean), +[kevin1018](https://github.com/kevin1018) + +[Full Changelog](https://github.com/redis/hiredis/compare/v0.14.1...v1.0.0) + +**BREAKING CHANGES**: + +* `redisOptions` now has two timeout fields. One for connecting, and one for commands. If you're presently using `options->timeout` you will need to change it to use `options->connect_timeout`. (See [example](https://github.com/redis/hiredis/commit/38b5ae543f5c99eb4ccabbe277770fc6bc81226f#diff-86ba39d37aa829c8c82624cce4f049fbL36)) + +* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now protocol errors. This is consistent + with the RESP specification. On 32-bit platforms, the upper bound is lowered to `SIZE_MAX`. + +* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter. + +**New features:** +- Support for RESP3 + [\#697](https://github.com/redis/hiredis/pull/697), + [\#805](https://github.com/redis/hiredis/pull/805), + [\#819](https://github.com/redis/hiredis/pull/819), + [\#841](https://github.com/redis/hiredis/pull/841) + ([Yossi Gottlieb](https://github.com/yossigo), [Michael Grunder](https://github.com/michael-grunder)) +- Support for SSL connections + [\#645](https://github.com/redis/hiredis/pull/645), + [\#699](https://github.com/redis/hiredis/pull/699), + [\#702](https://github.com/redis/hiredis/pull/702), + [\#708](https://github.com/redis/hiredis/pull/708), + [\#711](https://github.com/redis/hiredis/pull/711), + [\#821](https://github.com/redis/hiredis/pull/821), + [more](https://github.com/redis/hiredis/pulls?q=is%3Apr+is%3Amerged+SSL) + ([Mark Nunberg](https://github.com/mnunberg), [Yossi Gottlieb](https://github.com/yossigo)) +- Run-time allocator injection + [\#800](https://github.com/redis/hiredis/pull/800) + ([Michael Grunder](https://github.com/michael-grunder)) +- Improved Windows support (including MinGW and Windows CI) + [\#652](https://github.com/redis/hiredis/pull/652), + [\#663](https://github.com/redis/hiredis/pull/663) + ([Marcus Geelnard](https://www.bitsnbites.eu/author/m/)) +- Adds support for distinct connect and command timeouts + [\#839](https://github.com/redis/hiredis/pull/839), + [\#829](https://github.com/redis/hiredis/pull/829) + ([Valentino Geron](https://github.com/valentinogeron)) +- Add generic pointer and destructor to `redisContext` that users can use for context. + [\#855](https://github.com/redis/hiredis/pull/855) + ([Michael Grunder](https://github.com/michael-grunder)) + +**Closed issues (that involved code changes):** + +- Makefile does not install TLS libraries [\#809](https://github.com/redis/hiredis/issues/809) +- redisConnectWithOptions should not set command timeout [\#722](https://github.com/redis/hiredis/issues/722), [\#829](https://github.com/redis/hiredis/pull/829) ([valentinogeron](https://github.com/valentinogeron)) +- Fix integer overflow in `sdsrange` [\#827](https://github.com/redis/hiredis/issues/827) +- INFO & CLUSTER commands failed when using RESP3 [\#802](https://github.com/redis/hiredis/issues/802) +- Windows compatibility patches [\#687](https://github.com/redis/hiredis/issues/687), [\#838](https://github.com/redis/hiredis/issues/838), [\#842](https://github.com/redis/hiredis/issues/842) +- RESP3 PUSH messages incorrectly use pending callback [\#825](https://github.com/redis/hiredis/issues/825) +- Asynchronous PSUBSCRIBE command fails when using RESP3 [\#815](https://github.com/redis/hiredis/issues/815) +- New SSL API [\#804](https://github.com/redis/hiredis/issues/804), [\#813](https://github.com/redis/hiredis/issues/813) +- Hard-coded limit of nested reply depth [\#794](https://github.com/redis/hiredis/issues/794) +- Fix TCP_NODELAY in Windows/OSX [\#679](https://github.com/redis/hiredis/issues/679), [\#690](https://github.com/redis/hiredis/issues/690), [\#779](https://github.com/redis/hiredis/issues/779), [\#785](https://github.com/redis/hiredis/issues/785), +- Added timers to libev adapter. [\#778](https://github.com/redis/hiredis/issues/778), [\#795](https://github.com/redis/hiredis/pull/795) +- Initialization discards const qualifier [\#777](https://github.com/redis/hiredis/issues/777) +- \[BUG\]\[MinGW64\] Error setting socket timeout [\#775](https://github.com/redis/hiredis/issues/775) +- undefined reference to hi_malloc [\#769](https://github.com/redis/hiredis/issues/769) +- hiredis pkg-config file incorrectly ignores multiarch libdir spec'n [\#767](https://github.com/redis/hiredis/issues/767) +- Don't use -G to build shared object on Solaris [\#757](https://github.com/redis/hiredis/issues/757) +- error when make USE\_SSL=1 [\#748](https://github.com/redis/hiredis/issues/748) +- Allow to change SSL Mode [\#646](https://github.com/redis/hiredis/issues/646) +- hiredis/adapters/libevent.h memleak [\#618](https://github.com/redis/hiredis/issues/618) +- redisLibuvPoll crash when server closes the connetion [\#545](https://github.com/redis/hiredis/issues/545) +- about redisAsyncDisconnect question [\#518](https://github.com/redis/hiredis/issues/518) +- hiredis adapters libuv error for help [\#508](https://github.com/redis/hiredis/issues/508) +- API/ABI changes analysis [\#506](https://github.com/redis/hiredis/issues/506) +- Memory leak patch in Redis [\#502](https://github.com/redis/hiredis/issues/502) +- Remove the depth limitation [\#421](https://github.com/redis/hiredis/issues/421) + +**Merged pull requests:** + +- Move SSL management to a distinct private pointer [\#855](https://github.com/redis/hiredis/pull/855) ([michael-grunder](https://github.com/michael-grunder)) +- Move include to sockcompat.h to maintain style [\#850](https://github.com/redis/hiredis/pull/850) ([michael-grunder](https://github.com/michael-grunder)) +- Remove erroneous tag and add license to push example [\#849](https://github.com/redis/hiredis/pull/849) ([michael-grunder](https://github.com/michael-grunder)) +- fix windows compiling with mingw [\#848](https://github.com/redis/hiredis/pull/848) ([rmalizia44](https://github.com/rmalizia44)) +- Some Windows quality of life improvements. [\#846](https://github.com/redis/hiredis/pull/846) ([michael-grunder](https://github.com/michael-grunder)) +- Use \_WIN32 define instead of WIN32 [\#845](https://github.com/redis/hiredis/pull/845) ([michael-grunder](https://github.com/michael-grunder)) +- Non Linux CI fixes [\#844](https://github.com/redis/hiredis/pull/844) ([michael-grunder](https://github.com/michael-grunder)) +- Resp3 oob push support [\#841](https://github.com/redis/hiredis/pull/841) ([michael-grunder](https://github.com/michael-grunder)) +- fix \#785: defer TCP\_NODELAY in async tcp connections [\#836](https://github.com/redis/hiredis/pull/836) ([OmriSteiner](https://github.com/OmriSteiner)) +- sdsrange overflow fix [\#830](https://github.com/redis/hiredis/pull/830) ([michael-grunder](https://github.com/michael-grunder)) +- Use explicit pointer casting for c++ compatibility [\#826](https://github.com/redis/hiredis/pull/826) ([aureus1](https://github.com/aureus1)) +- Document allocator injection and completeness fix in test.c [\#824](https://github.com/redis/hiredis/pull/824) ([michael-grunder](https://github.com/michael-grunder)) +- Use unique names for allocator struct members [\#823](https://github.com/redis/hiredis/pull/823) ([michael-grunder](https://github.com/michael-grunder)) +- New SSL API to replace redisSecureConnection\(\). [\#821](https://github.com/redis/hiredis/pull/821) ([yossigo](https://github.com/yossigo)) +- Add logic to handle RESP3 push messages [\#819](https://github.com/redis/hiredis/pull/819) ([michael-grunder](https://github.com/michael-grunder)) +- Use standrad isxdigit instead of custom helper function. [\#814](https://github.com/redis/hiredis/pull/814) ([tryfinally](https://github.com/tryfinally)) +- Fix missing SSL build/install options. [\#812](https://github.com/redis/hiredis/pull/812) ([yossigo](https://github.com/yossigo)) +- Add link to ABI tracker [\#808](https://github.com/redis/hiredis/pull/808) ([michael-grunder](https://github.com/michael-grunder)) +- Resp3 verbatim string support [\#805](https://github.com/redis/hiredis/pull/805) ([michael-grunder](https://github.com/michael-grunder)) +- Allow users to replace allocator and handle OOM everywhere. [\#800](https://github.com/redis/hiredis/pull/800) ([michael-grunder](https://github.com/michael-grunder)) +- Remove nested depth limitation. [\#797](https://github.com/redis/hiredis/pull/797) ([michael-grunder](https://github.com/michael-grunder)) +- Attempt to fix compilation on Solaris [\#796](https://github.com/redis/hiredis/pull/796) ([michael-grunder](https://github.com/michael-grunder)) +- Support timeouts in libev adapater [\#795](https://github.com/redis/hiredis/pull/795) ([michael-grunder](https://github.com/michael-grunder)) +- Fix pkgconfig when installing to a custom lib dir [\#793](https://github.com/redis/hiredis/pull/793) ([michael-grunder](https://github.com/michael-grunder)) +- Fix USE\_SSL=1 make/cmake on OSX and CMake tests [\#789](https://github.com/redis/hiredis/pull/789) ([michael-grunder](https://github.com/michael-grunder)) +- Use correct libuv call on Windows [\#784](https://github.com/redis/hiredis/pull/784) ([michael-grunder](https://github.com/michael-grunder)) +- Added CMake package config and fixed hiredis\_ssl on Windows [\#783](https://github.com/redis/hiredis/pull/783) ([michael-grunder](https://github.com/michael-grunder)) +- CMake: Set hiredis\_ssl shared object version. [\#780](https://github.com/redis/hiredis/pull/780) ([yossigo](https://github.com/yossigo)) +- Win32 tests and timeout fix [\#776](https://github.com/redis/hiredis/pull/776) ([michael-grunder](https://github.com/michael-grunder)) +- Provides an optional cleanup callback for async data. [\#768](https://github.com/redis/hiredis/pull/768) ([heronr](https://github.com/heronr)) +- Housekeeping fixes [\#764](https://github.com/redis/hiredis/pull/764) ([michael-grunder](https://github.com/michael-grunder)) +- install alloc.h [\#756](https://github.com/redis/hiredis/pull/756) ([ch1aki](https://github.com/ch1aki)) +- fix spelling mistakes [\#746](https://github.com/redis/hiredis/pull/746) ([ShooterIT](https://github.com/ShooterIT)) +- Free the reply in redisGetReply when passed NULL [\#741](https://github.com/redis/hiredis/pull/741) ([michael-grunder](https://github.com/michael-grunder)) +- Fix dead code in sslLogCallback relating to should\_log variable. [\#737](https://github.com/redis/hiredis/pull/737) ([natoscott](https://github.com/natoscott)) +- Fix typo in dict.c. [\#731](https://github.com/redis/hiredis/pull/731) ([Kevin-Xi](https://github.com/Kevin-Xi)) +- Adding an option to DISABLE\_TESTS [\#727](https://github.com/redis/hiredis/pull/727) ([pbotros](https://github.com/pbotros)) +- Update README with SSL support. [\#720](https://github.com/redis/hiredis/pull/720) ([yossigo](https://github.com/yossigo)) +- Fixes leaks in unit tests [\#715](https://github.com/redis/hiredis/pull/715) ([michael-grunder](https://github.com/michael-grunder)) +- SSL Tests [\#711](https://github.com/redis/hiredis/pull/711) ([yossigo](https://github.com/yossigo)) +- SSL Reorganization [\#708](https://github.com/redis/hiredis/pull/708) ([yossigo](https://github.com/yossigo)) +- Fix MSVC build. [\#706](https://github.com/redis/hiredis/pull/706) ([yossigo](https://github.com/yossigo)) +- SSL: Properly report SSL\_connect\(\) errors. [\#702](https://github.com/redis/hiredis/pull/702) ([yossigo](https://github.com/yossigo)) +- Silent SSL trace to stdout by default. [\#699](https://github.com/redis/hiredis/pull/699) ([yossigo](https://github.com/yossigo)) +- Port RESP3 support from Redis. [\#697](https://github.com/redis/hiredis/pull/697) ([yossigo](https://github.com/yossigo)) +- Removed whitespace before newline [\#691](https://github.com/redis/hiredis/pull/691) ([Miniwoffer](https://github.com/Miniwoffer)) +- Add install adapters header files [\#688](https://github.com/redis/hiredis/pull/688) ([kevin1018](https://github.com/kevin1018)) +- Remove unnecessary null check before free [\#684](https://github.com/redis/hiredis/pull/684) ([qlyoung](https://github.com/qlyoung)) +- redisReaderGetReply leak memory [\#671](https://github.com/redis/hiredis/pull/671) ([movebean](https://github.com/movebean)) +- fix timeout code in windows [\#670](https://github.com/redis/hiredis/pull/670) ([jman-krafton](https://github.com/jman-krafton)) +- test: fix errstr matching for musl libc [\#665](https://github.com/redis/hiredis/pull/665) ([ghost](https://github.com/ghost)) +- Windows: MinGW fixes and Windows Travis builders [\#663](https://github.com/redis/hiredis/pull/663) ([mbitsnbites](https://github.com/mbitsnbites)) +- The setsockopt and getsockopt API diffs from BSD socket and WSA one [\#662](https://github.com/redis/hiredis/pull/662) ([dragonation](https://github.com/dragonation)) +- Fix Compile Error On Windows \(Visual Studio\) [\#658](https://github.com/redis/hiredis/pull/658) ([jinjiazhang](https://github.com/jinjiazhang)) +- Fix NXDOMAIN test case [\#653](https://github.com/redis/hiredis/pull/653) ([michael-grunder](https://github.com/michael-grunder)) +- Add MinGW support [\#652](https://github.com/redis/hiredis/pull/652) ([mbitsnbites](https://github.com/mbitsnbites)) +- SSL Support [\#645](https://github.com/redis/hiredis/pull/645) ([mnunberg](https://github.com/mnunberg)) +- Fix Invalid argument after redisAsyncConnectUnix [\#644](https://github.com/redis/hiredis/pull/644) ([codehz](https://github.com/codehz)) +- Makefile: use predefined AR [\#632](https://github.com/redis/hiredis/pull/632) ([Mic92](https://github.com/Mic92)) +- FreeBSD build fix [\#628](https://github.com/redis/hiredis/pull/628) ([devnexen](https://github.com/devnexen)) +- Fix errors not propagating properly with libuv.h. [\#624](https://github.com/redis/hiredis/pull/624) ([yossigo](https://github.com/yossigo)) +- Update README.md [\#621](https://github.com/redis/hiredis/pull/621) ([Crunsher](https://github.com/Crunsher)) +- Fix redisBufferRead documentation [\#620](https://github.com/redis/hiredis/pull/620) ([hacst](https://github.com/hacst)) +- Add CPPFLAGS to REAL\_CFLAGS [\#614](https://github.com/redis/hiredis/pull/614) ([thomaslee](https://github.com/thomaslee)) +- Update createArray to take size\_t [\#597](https://github.com/redis/hiredis/pull/597) ([justinbrewer](https://github.com/justinbrewer)) +- fix common realloc mistake and add null check more [\#580](https://github.com/redis/hiredis/pull/580) ([charsyam](https://github.com/charsyam)) +- Proper error reporting for connect failures [\#578](https://github.com/redis/hiredis/pull/578) ([mnunberg](https://github.com/mnunberg)) + +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* + +## [1.0.0-rc1](https://github.com/redis/hiredis/tree/v1.0.0-rc1) - (2020-07-29) + +_Note: There were no changes to code between v1.0.0-rc1 and v1.0.0 so see v1.0.0 for changelog_ + +### 0.14.1 (2020-03-13) + +* Adds safe allocation wrappers (CVE-2020-7105, #747, #752) (Michael Grunder) + +### 0.14.0 (2018-09-25) +**BREAKING CHANGES**: + +* Change `redisReply.len` to `size_t`, as it denotes the the size of a string + + User code should compare this to `size_t` values as well. + If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before. + +* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b]) +* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537]) +* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622]) +* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8]) +* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8]) +* Fix bulk and multi-bulk length truncation (Justin Brewer [109197]) +* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94]) +* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6]) +* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1]) +* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b]) +* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96]) +* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234]) +* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129]) +* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c]) +* Fix libevent leak (zfz [515228]) +* Clean up GCC warning (Ichito Nagata [2ec774]) +* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88]) +* Solaris compilation fix (Donald Whyte [41b07d]) +* Reorder linker arguments when building examples (Tustfarm-heart [06eedd]) +* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999]) +* libuv use after free fix (Paul Scott [cbb956]) +* Properly close socket fd on reconnect attempt (WSL [64d1ec]) +* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78]) +* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5]) +* Update libevent (Chris Xin [386802]) +* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e]) +* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6]) +* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3]) +* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb]) +* Compatibility fix for strerror_r (Tom Lee [bb1747]) +* Properly detect integer parse/overflow errors (Justin Brewer [93421f]) +* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40]) +* Catch a buffer overflow when formatting the error message +* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13 +* Fix warnings, when compiled with -Wshadow +* Make hiredis compile in Cygwin on Windows, now CI-tested +* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now + protocol errors. This is consistent with the RESP specification. On 32-bit + platforms, the upper bound is lowered to `SIZE_MAX`. + +* Remove backwards compatibility macro's + +This removes the following old function aliases, use the new name now: + +| Old | New | +| --------------------------- | ---------------------- | +| redisReplyReaderCreate | redisReaderCreate | +| redisReplyReaderCreate | redisReaderCreate | +| redisReplyReaderFree | redisReaderFree | +| redisReplyReaderFeed | redisReaderFeed | +| redisReplyReaderGetReply | redisReaderGetReply | +| redisReplyReaderSetPrivdata | redisReaderSetPrivdata | +| redisReplyReaderGetObject | redisReaderGetObject | +| redisReplyReaderGetError | redisReaderGetError | + +* The `DEBUG` variable in the Makefile was renamed to `DEBUG_FLAGS` + +Previously it broke some builds for people that had `DEBUG` set to some arbitrary value, +due to debugging other software. +By renaming we avoid unintentional name clashes. + +Simply rename `DEBUG` to `DEBUG_FLAGS` in your environment to make it working again. + +### 0.13.3 (2015-09-16) + +* Revert "Clear `REDIS_CONNECTED` flag when connection is closed". +* Make tests pass on FreeBSD (Thanks, Giacomo Olgeni) + + +If the `REDIS_CONNECTED` flag is cleared, +the async onDisconnect callback function will never be called. +This causes problems as the disconnect is never reported back to the user. + +### 0.13.2 (2015-08-25) + +* Prevent crash on pending replies in async code (Thanks, @switch-st) +* Clear `REDIS_CONNECTED` flag when connection is closed (Thanks, Jerry Jacobs) +* Add MacOS X addapter (Thanks, @dizzus) +* Add Qt adapter (Thanks, Pietro Cerutti) +* Add Ivykis adapter (Thanks, Gergely Nagy) + +All adapters are provided as is and are only tested where possible. + +### 0.13.1 (2015-05-03) + +This is a bug fix release. +The new `reconnect` method introduced new struct members, which clashed with pre-defined names in pre-C99 code. +Another commit forced C99 compilation just to make it work, but of course this is not desirable for outside projects. +Other non-C99 code can now use hiredis as usual again. +Sorry for the inconvenience. + +* Fix memory leak in async reply handling (Salvatore Sanfilippo) +* Rename struct member to avoid name clash with pre-c99 code (Alex Balashov, ncopa) + +### 0.13.0 (2015-04-16) + +This release adds a minimal Windows compatibility layer. +The parser, standalone since v0.12.0, can now be compiled on Windows +(and thus used in other client libraries as well) + +* Windows compatibility layer for parser code (tzickel) +* Properly escape data printed to PKGCONF file (Dan Skorupski) +* Fix tests when assert() undefined (Keith Bennett, Matt Stancliff) +* Implement a reconnect method for the client context, this changes the structure of `redisContext` (Aaron Bedra) + +### 0.12.1 (2015-01-26) + +* Fix `make install`: DESTDIR support, install all required files, install PKGCONF in proper location +* Fix `make test` as 32 bit build on 64 bit platform + +### 0.12.0 (2015-01-22) + +* Add optional KeepAlive support + +* Try again on EINTR errors + +* Add libuv adapter + +* Add IPv6 support + +* Remove possibility of multiple close on same fd + +* Add ability to bind source address on connect + +* Add redisConnectFd() and redisFreeKeepFd() + +* Fix getaddrinfo() memory leak + +* Free string if it is unused (fixes memory leak) + +* Improve redisAppendCommandArgv performance 2.5x + +* Add support for SO_REUSEADDR + +* Fix redisvFormatCommand format parsing + +* Add GLib 2.0 adapter + +* Refactor reading code into read.c + +* Fix errno error buffers to not clobber errors + +* Generate pkgconf during build + +* Silence _BSD_SOURCE warnings + +* Improve digit counting for multibulk creation + + +### 0.11.0 + +* Increase the maximum multi-bulk reply depth to 7. + +* Increase the read buffer size from 2k to 16k. + +* Use poll(2) instead of select(2) to support large fds (>= 1024). + +### 0.10.1 + +* Makefile overhaul. Important to check out if you override one or more + variables using environment variables or via arguments to the "make" tool. + +* Issue #45: Fix potential memory leak for a multi bulk reply with 0 elements + being created by the default reply object functions. + +* Issue #43: Don't crash in an asynchronous context when Redis returns an error + reply after the connection has been made (this happens when the maximum + number of connections is reached). + +### 0.10.0 + +* See commit log. diff --git a/ext/hiredis-1.0.2/CMakeLists.txt b/ext/hiredis-1.0.2/CMakeLists.txt new file mode 100644 index 000000000..f86c9b70b --- /dev/null +++ b/ext/hiredis-1.0.2/CMakeLists.txt @@ -0,0 +1,165 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0) +INCLUDE(GNUInstallDirs) +PROJECT(hiredis) + +OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF) +OPTION(DISABLE_TESTS "If tests should be compiled or not" OFF) +OPTION(ENABLE_SSL_TESTS, "Should we test SSL connections" OFF) + +MACRO(getVersionBit name) + SET(VERSION_REGEX "^#define ${name} (.+)$") + FILE(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/hiredis.h" + VERSION_BIT REGEX ${VERSION_REGEX}) + STRING(REGEX REPLACE ${VERSION_REGEX} "\\1" ${name} "${VERSION_BIT}") +ENDMACRO(getVersionBit) + +getVersionBit(HIREDIS_MAJOR) +getVersionBit(HIREDIS_MINOR) +getVersionBit(HIREDIS_PATCH) +getVersionBit(HIREDIS_SONAME) +SET(VERSION "${HIREDIS_MAJOR}.${HIREDIS_MINOR}.${HIREDIS_PATCH}") +MESSAGE("Detected version: ${VERSION}") + +PROJECT(hiredis VERSION "${VERSION}") + +SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples") + +SET(hiredis_sources + alloc.c + async.c + dict.c + hiredis.c + net.c + read.c + sds.c + sockcompat.c) + +SET(hiredis_sources ${hiredis_sources}) + +IF(WIN32) + ADD_COMPILE_DEFINITIONS(_CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN) +ENDIF() + +ADD_LIBRARY(hiredis SHARED ${hiredis_sources}) + +SET_TARGET_PROPERTIES(hiredis + PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE + VERSION "${HIREDIS_SONAME}") +IF(WIN32 OR MINGW) + TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32) +ENDIF() + +TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC $ $) + +CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY) + +INSTALL(TARGETS hiredis + EXPORT hiredis-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +INSTALL(FILES hiredis.h read.h sds.h async.h alloc.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) + +INSTALL(DIRECTORY adapters + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) + +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + +export(EXPORT hiredis-targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis-targets.cmake" + NAMESPACE hiredis::) + +SET(CMAKE_CONF_INSTALL_DIR share/hiredis) +SET(INCLUDE_INSTALL_DIR include) +include(CMakePackageConfigHelpers) +configure_package_config_file(hiredis-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake + INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR} + PATH_VARS INCLUDE_INSTALL_DIR) + +INSTALL(EXPORT hiredis-targets + FILE hiredis-targets.cmake + NAMESPACE hiredis:: + DESTINATION ${CMAKE_CONF_INSTALL_DIR}) + +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake + DESTINATION ${CMAKE_CONF_INSTALL_DIR}) + + +IF(ENABLE_SSL) + IF (NOT OPENSSL_ROOT_DIR) + IF (APPLE) + SET(OPENSSL_ROOT_DIR "/usr/local/opt/openssl") + ENDIF() + ENDIF() + FIND_PACKAGE(OpenSSL REQUIRED) + SET(hiredis_ssl_sources + ssl.c) + ADD_LIBRARY(hiredis_ssl SHARED + ${hiredis_ssl_sources}) + + IF (APPLE) + SET_PROPERTY(TARGET hiredis_ssl PROPERTY LINK_FLAGS "-Wl,-undefined -Wl,dynamic_lookup") + ENDIF() + + SET_TARGET_PROPERTIES(hiredis_ssl + PROPERTIES + WINDOWS_EXPORT_ALL_SYMBOLS TRUE + VERSION "${HIREDIS_SONAME}") + + TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}") + TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES}) + IF (WIN32 OR MINGW) + TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE hiredis) + ENDIF() + CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY) + + INSTALL(TARGETS hiredis_ssl + EXPORT hiredis_ssl-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + + INSTALL(FILES hiredis_ssl.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) + + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + + export(EXPORT hiredis_ssl-targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-targets.cmake" + NAMESPACE hiredis::) + + SET(CMAKE_CONF_INSTALL_DIR share/hiredis_ssl) + configure_package_config_file(hiredis_ssl-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake + INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR} + PATH_VARS INCLUDE_INSTALL_DIR) + + INSTALL(EXPORT hiredis_ssl-targets + FILE hiredis_ssl-targets.cmake + NAMESPACE hiredis:: + DESTINATION ${CMAKE_CONF_INSTALL_DIR}) + + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake + DESTINATION ${CMAKE_CONF_INSTALL_DIR}) +ENDIF() + +IF(NOT DISABLE_TESTS) + ENABLE_TESTING() + ADD_EXECUTABLE(hiredis-test test.c) + IF(ENABLE_SSL_TESTS) + ADD_DEFINITIONS(-DHIREDIS_TEST_SSL=1) + TARGET_LINK_LIBRARIES(hiredis-test hiredis hiredis_ssl) + ELSE() + TARGET_LINK_LIBRARIES(hiredis-test hiredis) + ENDIF() + ADD_TEST(NAME hiredis-test + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh) +ENDIF() + +# Add examples +IF(ENABLE_EXAMPLES) + ADD_SUBDIRECTORY(examples) +ENDIF(ENABLE_EXAMPLES) diff --git a/ext/hiredis-1.0.2/COPYING b/ext/hiredis-1.0.2/COPYING new file mode 100644 index 000000000..a5fc97395 --- /dev/null +++ b/ext/hiredis-1.0.2/COPYING @@ -0,0 +1,29 @@ +Copyright (c) 2009-2011, Salvatore Sanfilippo +Copyright (c) 2010-2011, Pieter Noordhuis + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Redis nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ext/hiredis-1.0.2/Makefile b/ext/hiredis-1.0.2/Makefile new file mode 100644 index 000000000..a8d37a2eb --- /dev/null +++ b/ext/hiredis-1.0.2/Makefile @@ -0,0 +1,308 @@ +# Hiredis Makefile +# Copyright (C) 2010-2011 Salvatore Sanfilippo +# Copyright (C) 2010-2011 Pieter Noordhuis +# This file is released under the BSD license, see the COPYING file + +OBJ=alloc.o net.o hiredis.o sds.o async.o read.o sockcompat.o +SSL_OBJ=ssl.o +EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib hiredis-example-push +ifeq ($(USE_SSL),1) +EXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl +endif +TESTS=hiredis-test +LIBNAME=libhiredis +PKGCONFNAME=hiredis.pc +SSL_LIBNAME=libhiredis_ssl +SSL_PKGCONFNAME=hiredis_ssl.pc + +HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}') +HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}') +HIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}') +HIREDIS_SONAME=$(shell grep HIREDIS_SONAME hiredis.h | awk '{print $$3}') + +# Installation related variables and target +PREFIX?=/usr/local +INCLUDE_PATH?=include/hiredis +LIBRARY_PATH?=lib +PKGCONF_PATH?=pkgconfig +INSTALL_INCLUDE_PATH= $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH) +INSTALL_LIBRARY_PATH= $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH) +INSTALL_PKGCONF_PATH= $(INSTALL_LIBRARY_PATH)/$(PKGCONF_PATH) + +# redis-server configuration used for testing +REDIS_PORT=56379 +REDIS_SERVER=redis-server +define REDIS_TEST_CONFIG + daemonize yes + pidfile /tmp/hiredis-test-redis.pid + port $(REDIS_PORT) + bind 127.0.0.1 + unixsocket /tmp/hiredis-test-redis.sock +endef +export REDIS_TEST_CONFIG + +# Fallback to gcc when $CC is not in $PATH. +CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc') +CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++') +OPTIMIZATION?=-O3 +WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers +DEBUG_FLAGS?= -g -ggdb +REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS) +REAL_LDFLAGS=$(LDFLAGS) + +DYLIBSUFFIX=so +STLIBSUFFIX=a +DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME) +DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR) +DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX) + +DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) +STLIBNAME=$(LIBNAME).$(STLIBSUFFIX) +STLIB_MAKE_CMD=$(AR) rcs + +SSL_DYLIB_MINOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME) +SSL_DYLIB_MAJOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR) +SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX) +SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX) +SSL_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(SSL_DYLIB_MINOR_NAME) + +# Platform-specific overrides +uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') + +USE_SSL?=0 + +# This is required for test.c only +ifeq ($(USE_SSL),1) + CFLAGS+=-DHIREDIS_TEST_SSL +endif + +ifeq ($(uname_S),Linux) + SSL_LDFLAGS=-lssl -lcrypto +else + OPENSSL_PREFIX?=/usr/local/opt/openssl + CFLAGS+=-I$(OPENSSL_PREFIX)/include + SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto +endif + +ifeq ($(uname_S),SunOS) + IS_SUN_CC=$(shell sh -c '$(CC) -V 2>&1 |egrep -i -c "sun|studio"') + ifeq ($(IS_SUN_CC),1) + SUN_SHARED_FLAG=-G + else + SUN_SHARED_FLAG=-shared + endif + REAL_LDFLAGS+= -ldl -lnsl -lsocket + DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS) + SSL_DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(SSL_DYLIBNAME) -h $(SSL_DYLIB_MINOR_NAME) $(LDFLAGS) $(SSL_LDFLAGS) +endif +ifeq ($(uname_S),Darwin) + DYLIBSUFFIX=dylib + DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX) + DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) + SSL_DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME) -o $(SSL_DYLIBNAME) $(LDFLAGS) $(SSL_LDFLAGS) + DYLIB_PLUGIN=-Wl,-undefined -Wl,dynamic_lookup +endif + +all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME) +ifeq ($(USE_SSL),1) +all: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME) +endif + +# Deps (use make dep to generate this) +alloc.o: alloc.c fmacros.h alloc.h +async.o: async.c fmacros.h alloc.h async.h hiredis.h read.h sds.h net.h dict.c dict.h win32.h async_private.h +dict.o: dict.c fmacros.h alloc.h dict.h +hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h alloc.h net.h async.h win32.h +net.o: net.c fmacros.h net.h hiredis.h read.h sds.h alloc.h sockcompat.h win32.h +read.o: read.c fmacros.h alloc.h read.h sds.h win32.h +sds.o: sds.c sds.h sdsalloc.h alloc.h +sockcompat.o: sockcompat.c sockcompat.h +ssl.o: ssl.c hiredis.h read.h sds.h alloc.h async.h win32.h async_private.h +test.o: test.c fmacros.h hiredis.h read.h sds.h alloc.h net.h sockcompat.h win32.h + +$(DYLIBNAME): $(OBJ) + $(DYLIB_MAKE_CMD) -o $(DYLIBNAME) $(OBJ) $(REAL_LDFLAGS) + +$(STLIBNAME): $(OBJ) + $(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ) + +$(SSL_DYLIBNAME): $(SSL_OBJ) + $(SSL_DYLIB_MAKE_CMD) $(DYLIB_PLUGIN) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(LDFLAGS) $(SSL_LDFLAGS) + +$(SSL_STLIBNAME): $(SSL_OBJ) + $(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ) + +dynamic: $(DYLIBNAME) +static: $(STLIBNAME) +ifeq ($(USE_SSL),1) +dynamic: $(SSL_DYLIBNAME) +static: $(SSL_STLIBNAME) +endif + +# Binaries: +hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME) + $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(REAL_LDFLAGS) + +hiredis-example-libevent-ssl: examples/example-libevent-ssl.c adapters/libevent.h $(STLIBNAME) $(SSL_STLIBNAME) + $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS) + +hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME) + $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -lev $(STLIBNAME) $(REAL_LDFLAGS) + +hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME) + $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) $(REAL_LDFLAGS) + +hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME) + $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -livykis $(STLIBNAME) $(REAL_LDFLAGS) + +hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME) + $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME) $(REAL_LDFLAGS) + +hiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME) + $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS) + + +ifndef AE_DIR +hiredis-example-ae: + @echo "Please specify AE_DIR (e.g. /src)" + @false +else +hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME) + $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME) +endif + +ifndef LIBUV_DIR +hiredis-example-libuv: + @echo "Please specify LIBUV_DIR (e.g. ../libuv/)" + @false +else +hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME) + $(CC) -o examples/$@ $(REAL_CFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) $(REAL_LDFLAGS) +endif + +ifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),) +hiredis-example-qt: + @echo "Please specify QT_MOC, QT_INCLUDE_DIR AND QT_LIBRARY_DIR" + @false +else +hiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME) + $(QT_MOC) adapters/qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ + $(CXX) -x c++ -o qt-adapter-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore + $(QT_MOC) examples/example-qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ + $(CXX) -x c++ -o qt-example-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore + $(CXX) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore -L$(QT_LIBRARY_DIR) qt-adapter-moc.o qt-example-moc.o $< -pthread $(STLIBNAME) -lQtCore +endif + +hiredis-example: examples/example.c $(STLIBNAME) + $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS) + +hiredis-example-push: examples/example-push.c $(STLIBNAME) + $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS) + +examples: $(EXAMPLES) + +TEST_LIBS = $(STLIBNAME) +ifeq ($(USE_SSL),1) + TEST_LIBS += $(SSL_STLIBNAME) + TEST_LDFLAGS = $(SSL_LDFLAGS) -lssl -lcrypto -lpthread +endif + +hiredis-test: test.o $(TEST_LIBS) + $(CC) -o $@ $(REAL_CFLAGS) -I. $^ $(REAL_LDFLAGS) $(TEST_LDFLAGS) + +hiredis-%: %.o $(STLIBNAME) + $(CC) $(REAL_CFLAGS) -o $@ $< $(TEST_LIBS) $(REAL_LDFLAGS) + +test: hiredis-test + ./hiredis-test + +check: hiredis-test + TEST_SSL=$(USE_SSL) ./test.sh + +.c.o: + $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $< + +clean: + rm -rf $(DYLIBNAME) $(STLIBNAME) $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov + +dep: + $(CC) $(CPPFLAGS) $(CFLAGS) -MM *.c + +INSTALL?= cp -pPR + +$(PKGCONFNAME): hiredis.h + @echo "Generating $@ for pkgconfig..." + @echo prefix=$(PREFIX) > $@ + @echo exec_prefix=\$${prefix} >> $@ + @echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@ + @echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@ + @echo >> $@ + @echo Name: hiredis >> $@ + @echo Description: Minimalistic C client library for Redis. >> $@ + @echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@ + @echo Libs: -L\$${libdir} -lhiredis >> $@ + @echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@ + +$(SSL_PKGCONFNAME): hiredis_ssl.h + @echo "Generating $@ for pkgconfig..." + @echo prefix=$(PREFIX) > $@ + @echo exec_prefix=\$${prefix} >> $@ + @echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@ + @echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@ + @echo >> $@ + @echo Name: hiredis_ssl >> $@ + @echo Description: SSL Support for hiredis. >> $@ + @echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@ + @echo Requires: hiredis >> $@ + @echo Libs: -L\$${libdir} -lhiredis_ssl >> $@ + @echo Libs.private: -lssl -lcrypto >> $@ + +install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) + mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH) + $(INSTALL) hiredis.h async.h read.h sds.h alloc.h $(INSTALL_INCLUDE_PATH) + $(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters + $(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME) + cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME) + $(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH) + mkdir -p $(INSTALL_PKGCONF_PATH) + $(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH) + +ifeq ($(USE_SSL),1) +install: install-ssl + +install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME) + mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH) + $(INSTALL) hiredis_ssl.h $(INSTALL_INCLUDE_PATH) + $(INSTALL) $(SSL_DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME) + cd $(INSTALL_LIBRARY_PATH) && ln -sf $(SSL_DYLIB_MINOR_NAME) $(SSL_DYLIBNAME) + $(INSTALL) $(SSL_STLIBNAME) $(INSTALL_LIBRARY_PATH) + mkdir -p $(INSTALL_PKGCONF_PATH) + $(INSTALL) $(SSL_PKGCONFNAME) $(INSTALL_PKGCONF_PATH) +endif + +32bit: + @echo "" + @echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386" + @echo "" + $(MAKE) CFLAGS="-m32" LDFLAGS="-m32" + +32bit-vars: + $(eval CFLAGS=-m32) + $(eval LDFLAGS=-m32) + +gprof: + $(MAKE) CFLAGS="-pg" LDFLAGS="-pg" + +gcov: + $(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs" + +coverage: gcov + make check + mkdir -p tmp/lcov + lcov -d . -c -o tmp/lcov/hiredis.info + genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info + +noopt: + $(MAKE) OPTIMIZATION="" + +.PHONY: all test check clean dep install 32bit 32bit-vars gprof gcov noopt diff --git a/ext/hiredis-1.0.2/README.md b/ext/hiredis-1.0.2/README.md new file mode 100644 index 000000000..c544d5718 --- /dev/null +++ b/ext/hiredis-1.0.2/README.md @@ -0,0 +1,664 @@ +[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis) + +**This Readme reflects the latest changed in the master branch. See [v1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) for the Readme and documentation for the latest release ([API/ABI history](https://abi-laboratory.pro/?view=timeline&l=hiredis)).** + +# HIREDIS + +Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database. + +It is minimalistic because it just adds minimal support for the protocol, but +at the same time it uses a high level printf-alike API in order to make it +much higher level than otherwise suggested by its minimal code base and the +lack of explicit bindings for every Redis command. + +Apart from supporting sending commands and receiving replies, it comes with +a reply parser that is decoupled from the I/O layer. It +is a stream parser designed for easy reusability, which can for instance be used +in higher level language bindings for efficient reply parsing. + +Hiredis only supports the binary-safe Redis protocol, so you can use it with any +Redis version >= 1.2.0. + +The library comes with multiple APIs. There is the +*synchronous API*, the *asynchronous API* and the *reply parsing API*. + +## Upgrading to `1.0.2` + +NOTE: v1.0.1 erroneously bumped SONAME, which is why it is skipped here. + +Version 1.0.2 is simply 1.0.0 with a fix for [CVE-2021-32765](https://github.com/redis/hiredis/security/advisories/GHSA-hfm9-39pp-55p2). They are otherwise identical. + +## Upgrading to `1.0.0` + +Version 1.0.0 marks the first stable release of Hiredis. +It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory. +It also bundles the updated `sds` library, to sync up with upstream and Redis. +For code changes see the [Changelog](CHANGELOG.md). + +_Note: As described below, a few member names have been changed but most applications should be able to upgrade with minor code changes and recompiling._ + +## IMPORTANT: Breaking changes from `0.14.1` -> `1.0.0` + +* `redisContext` has two additional members (`free_privdata`, and `privctx`). +* `redisOptions.timeout` has been renamed to `redisOptions.connect_timeout`, and we've added `redisOptions.command_timeout`. +* `redisReplyObjectFunctions.createArray` now takes `size_t` instead of `int` for its length parameter. + +## IMPORTANT: Breaking changes when upgrading from 0.13.x -> 0.14.x + +Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now +protocol errors. This is consistent with the RESP specification. On 32-bit +platforms, the upper bound is lowered to `SIZE_MAX`. + +Change `redisReply.len` to `size_t`, as it denotes the the size of a string + +User code should compare this to `size_t` values as well. If it was used to +compare to other values, casting might be necessary or can be removed, if +casting was applied before. + +## Upgrading from `<0.9.0` + +Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing +code using hiredis should not be a big pain. The key thing to keep in mind when +upgrading is that hiredis >= 0.9.0 uses a `redisContext*` to keep state, in contrast to +the stateless 0.0.1 that only has a file descriptor to work with. + +## Synchronous API + +To consume the synchronous API, there are only a few function calls that need to be introduced: + +```c +redisContext *redisConnect(const char *ip, int port); +void *redisCommand(redisContext *c, const char *format, ...); +void freeReplyObject(void *reply); +``` + +### Connecting + +The function `redisConnect` is used to create a so-called `redisContext`. The +context is where Hiredis holds state for a connection. The `redisContext` +struct has an integer `err` field that is non-zero when the connection is in +an error state. The field `errstr` will contain a string with a description of +the error. More information on errors can be found in the **Errors** section. +After trying to connect to Redis using `redisConnect` you should +check the `err` field to see if establishing the connection was successful: +```c +redisContext *c = redisConnect("127.0.0.1", 6379); +if (c == NULL || c->err) { + if (c) { + printf("Error: %s\n", c->errstr); + // handle error + } else { + printf("Can't allocate redis context\n"); + } +} +``` + +*Note: A `redisContext` is not thread-safe.* + +### Sending commands + +There are several ways to issue commands to Redis. The first that will be introduced is +`redisCommand`. This function takes a format similar to printf. In the simplest form, +it is used like this: +```c +reply = redisCommand(context, "SET foo bar"); +``` + +The specifier `%s` interpolates a string in the command, and uses `strlen` to +determine the length of the string: +```c +reply = redisCommand(context, "SET foo %s", value); +``` +When you need to pass binary safe strings in a command, the `%b` specifier can be +used. Together with a pointer to the string, it requires a `size_t` length argument +of the string: +```c +reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen); +``` +Internally, Hiredis splits the command in different arguments and will +convert it to the protocol used to communicate with Redis. +One or more spaces separates arguments, so you can use the specifiers +anywhere in an argument: +```c +reply = redisCommand(context, "SET key:%s %s", myid, value); +``` + +### Using replies + +The return value of `redisCommand` holds a reply when the command was +successfully executed. When an error occurs, the return value is `NULL` and +the `err` field in the context will be set (see section on **Errors**). +Once an error is returned the context cannot be reused and you should set up +a new connection. + +The standard replies that `redisCommand` are of the type `redisReply`. The +`type` field in the `redisReply` should be used to test what kind of reply +was received: + +### RESP2 + +* **`REDIS_REPLY_STATUS`**: + * The command replied with a status reply. The status string can be accessed using `reply->str`. + The length of this string can be accessed using `reply->len`. + +* **`REDIS_REPLY_ERROR`**: + * The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`. + +* **`REDIS_REPLY_INTEGER`**: + * The command replied with an integer. The integer value can be accessed using the + `reply->integer` field of type `long long`. + +* **`REDIS_REPLY_NIL`**: + * The command replied with a **nil** object. There is no data to access. + +* **`REDIS_REPLY_STRING`**: + * A bulk (string) reply. The value of the reply can be accessed using `reply->str`. + The length of this string can be accessed using `reply->len`. + +* **`REDIS_REPLY_ARRAY`**: + * A multi bulk reply. The number of elements in the multi bulk reply is stored in + `reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well + and can be accessed via `reply->element[..index..]`. + Redis may reply with nested arrays but this is fully supported. + +### RESP3 + +Hiredis also supports every new `RESP3` data type which are as follows. For more information about the protocol see the `RESP3` [specification.](https://github.com/antirez/RESP3/blob/master/spec.md) + +* **`REDIS_REPLY_DOUBLE`**: + * The command replied with a double-precision floating point number. + The value is stored as a string in the `str` member, and can be converted with `strtod` or similar. + +* **`REDIS_REPLY_BOOL`**: + * A boolean true/false reply. + The value is stored in the `integer` member and will be either `0` or `1`. + +* **`REDIS_REPLY_MAP`**: + * An array with the added invariant that there will always be an even number of elements. + The MAP is functionally equivelant to `REDIS_REPLY_ARRAY` except for the previously mentioned invariant. + +* **`REDIS_REPLY_SET`**: + * An array response where each entry is unique. + Like the MAP type, the data is identical to an array response except there are no duplicate values. + +* **`REDIS_REPLY_PUSH`**: + * An array that can be generated spontaneously by Redis. + This array response will always contain at least two subelements. The first contains the type of `PUSH` message (e.g. `message`, or `invalidate`), and the second being a sub-array with the `PUSH` payload itself. + +* **`REDIS_REPLY_ATTR`**: + * An array structurally identical to a `MAP` but intended as meta-data about a reply. + _As of Redis 6.0.6 this reply type is not used in Redis_ + +* **`REDIS_REPLY_BIGNUM`**: + * A string representing an arbitrarily large signed or unsigned integer value. + The number will be encoded as a string in the `str` member of `redisReply`. + +* **`REDIS_REPLY_VERB`**: + * A verbatim string, intended to be presented to the user without modification. + The string payload is stored in the `str` memeber, and type data is stored in the `vtype` member (e.g. `txt` for raw text or `md` for markdown). + +Replies should be freed using the `freeReplyObject()` function. +Note that this function will take care of freeing sub-reply objects +contained in arrays and nested arrays, so there is no need for the user to +free the sub replies (it is actually harmful and will corrupt the memory). + +**Important:** the current version of hiredis (1.0.0) frees replies when the +asynchronous API is used. This means you should not call `freeReplyObject` when +you use this API. The reply is cleaned up by hiredis _after_ the callback +returns. We may introduce a flag to make this configurable in future versions of the library. + +### Cleaning up + +To disconnect and free the context the following function can be used: +```c +void redisFree(redisContext *c); +``` +This function immediately closes the socket and then frees the allocations done in +creating the context. + +### Sending commands (cont'd) + +Together with `redisCommand`, the function `redisCommandArgv` can be used to issue commands. +It has the following prototype: +```c +void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); +``` +It takes the number of arguments `argc`, an array of strings `argv` and the lengths of the +arguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will +use `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments +need to be binary safe, the entire array of lengths `argvlen` should be provided. + +The return value has the same semantic as `redisCommand`. + +### Pipelining + +To explain how Hiredis supports pipelining in a blocking connection, there needs to be +understanding of the internal execution flow. + +When any of the functions in the `redisCommand` family is called, Hiredis first formats the +command according to the Redis protocol. The formatted command is then put in the output buffer +of the context. This output buffer is dynamic, so it can hold any number of commands. +After the command is put in the output buffer, `redisGetReply` is called. This function has the +following two execution paths: + +1. The input buffer is non-empty: + * Try to parse a single reply from the input buffer and return it + * If no reply could be parsed, continue at *2* +2. The input buffer is empty: + * Write the **entire** output buffer to the socket + * Read from the socket until a single reply could be parsed + +The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply +is expected on the socket. To pipeline commands, the only things that needs to be done is +filling up the output buffer. For this cause, two commands can be used that are identical +to the `redisCommand` family, apart from not returning a reply: +```c +void redisAppendCommand(redisContext *c, const char *format, ...); +void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); +``` +After calling either function one or more times, `redisGetReply` can be used to receive the +subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where +the latter means an error occurred while reading a reply. Just as with the other commands, +the `err` field in the context can be used to find out what the cause of this error is. + +The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and +a single call to `read(2)`): +```c +redisReply *reply; +redisAppendCommand(context,"SET foo bar"); +redisAppendCommand(context,"GET foo"); +redisGetReply(context,(void *)&reply); // reply for SET +freeReplyObject(reply); +redisGetReply(context,(void *)&reply); // reply for GET +freeReplyObject(reply); +``` +This API can also be used to implement a blocking subscriber: +```c +reply = redisCommand(context,"SUBSCRIBE foo"); +freeReplyObject(reply); +while(redisGetReply(context,(void *)&reply) == REDIS_OK) { + // consume message + freeReplyObject(reply); +} +``` +### Errors + +When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is +returned. The `err` field inside the context will be non-zero and set to one of the +following constants: + +* **`REDIS_ERR_IO`**: + There was an I/O error while creating the connection, trying to write + to the socket or read from the socket. If you included `errno.h` in your + application, you can use the global `errno` variable to find out what is + wrong. + +* **`REDIS_ERR_EOF`**: + The server closed the connection which resulted in an empty read. + +* **`REDIS_ERR_PROTOCOL`**: + There was an error while parsing the protocol. + +* **`REDIS_ERR_OTHER`**: + Any other error. Currently, it is only used when a specified hostname to connect + to cannot be resolved. + +In every case, the `errstr` field in the context will be set to hold a string representation +of the error. + +## Asynchronous API + +Hiredis comes with an asynchronous API that works easily with any event library. +Examples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html) +and [libevent](http://monkey.org/~provos/libevent/). + +### Connecting + +The function `redisAsyncConnect` can be used to establish a non-blocking connection to +Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field +should be checked after creation to see if there were errors creating the connection. +Because the connection that will be created is non-blocking, the kernel is not able to +instantly return if the specified host and port is able to accept a connection. + +*Note: A `redisAsyncContext` is not thread-safe.* + +```c +redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); +if (c->err) { + printf("Error: %s\n", c->errstr); + // handle error +} +``` + +The asynchronous context can hold a disconnect callback function that is called when the +connection is disconnected (either because of an error or per user request). This function should +have the following prototype: +```c +void(const redisAsyncContext *c, int status); +``` +On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the +user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err` +field in the context can be accessed to find out the cause of the error. + +The context object is always freed after the disconnect callback fired. When a reconnect is needed, +the disconnect callback is a good point to do so. + +Setting the disconnect callback can only be done once per context. For subsequent calls it will +return `REDIS_ERR`. The function to set the disconnect callback has the following prototype: +```c +int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); +``` +`ac->data` may be used to pass user data to this callback, the same can be done for redisConnectCallback. +### Sending commands and their callbacks + +In an asynchronous context, commands are automatically pipelined due to the nature of an event loop. +Therefore, unlike the synchronous API, there is only a single way to send commands. +Because commands are sent to Redis asynchronously, issuing a command requires a callback function +that is called when the reply is received. Reply callbacks should have the following prototype: +```c +void(redisAsyncContext *c, void *reply, void *privdata); +``` +The `privdata` argument can be used to curry arbitrary data to the callback from the point where +the command is initially queued for execution. + +The functions that can be used to issue commands in an asynchronous context are: +```c +int redisAsyncCommand( + redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, + const char *format, ...); +int redisAsyncCommandArgv( + redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, + int argc, const char **argv, const size_t *argvlen); +``` +Both functions work like their blocking counterparts. The return value is `REDIS_OK` when the command +was successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection +is being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is +returned on calls to the `redisAsyncCommand` family. + +If the reply for a command with a `NULL` callback is read, it is immediately freed. When the callback +for a command is non-`NULL`, the memory is freed immediately following the callback: the reply is only +valid for the duration of the callback. + +All pending callbacks are called with a `NULL` reply when the context encountered an error. + +### Disconnecting + +An asynchronous connection can be terminated using: +```c +void redisAsyncDisconnect(redisAsyncContext *ac); +``` +When this function is called, the connection is **not** immediately terminated. Instead, new +commands are no longer accepted and the connection is only terminated when all pending commands +have been written to the socket, their respective replies have been read and their respective +callbacks have been executed. After this, the disconnection callback is executed with the +`REDIS_OK` status and the context object is freed. + +### Hooking it up to event library *X* + +There are a few hooks that need to be set on the context object after it is created. +See the `adapters/` directory for bindings to *libev* and *libevent*. + +## Reply parsing API + +Hiredis comes with a reply parsing API that makes it easy for writing higher +level language bindings. + +The reply parsing API consists of the following functions: +```c +redisReader *redisReaderCreate(void); +void redisReaderFree(redisReader *reader); +int redisReaderFeed(redisReader *reader, const char *buf, size_t len); +int redisReaderGetReply(redisReader *reader, void **reply); +``` +The same set of functions are used internally by hiredis when creating a +normal Redis context, the above API just exposes it to the user for a direct +usage. + +### Usage + +The function `redisReaderCreate` creates a `redisReader` structure that holds a +buffer with unparsed data and state for the protocol parser. + +Incoming data -- most likely from a socket -- can be placed in the internal +buffer of the `redisReader` using `redisReaderFeed`. This function will make a +copy of the buffer pointed to by `buf` for `len` bytes. This data is parsed +when `redisReaderGetReply` is called. This function returns an integer status +and a reply object (as described above) via `void **reply`. The returned status +can be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went +wrong (either a protocol error, or an out of memory error). + +The parser limits the level of nesting for multi bulk payloads to 7. If the +multi bulk nesting level is higher than this, the parser returns an error. + +### Customizing replies + +The function `redisReaderGetReply` creates `redisReply` and makes the function +argument `reply` point to the created `redisReply` variable. For instance, if +the response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply` +will hold the status as a vanilla C string. However, the functions that are +responsible for creating instances of the `redisReply` can be customized by +setting the `fn` field on the `redisReader` struct. This should be done +immediately after creating the `redisReader`. + +For example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c) +uses customized reply object functions to create Ruby objects. + +### Reader max buffer + +Both when using the Reader API directly or when using it indirectly via a +normal Redis context, the redisReader structure uses a buffer in order to +accumulate data from the server. +Usually this buffer is destroyed when it is empty and is larger than 16 +KiB in order to avoid wasting memory in unused buffers + +However when working with very big payloads destroying the buffer may slow +down performances considerably, so it is possible to modify the max size of +an idle buffer changing the value of the `maxbuf` field of the reader structure +to the desired value. The special value of 0 means that there is no maximum +value for an idle buffer, so the buffer will never get freed. + +For instance if you have a normal Redis context you can set the maximum idle +buffer to zero (unlimited) just with: +```c +context->reader->maxbuf = 0; +``` +This should be done only in order to maximize performances when working with +large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again +as soon as possible in order to prevent allocation of useless memory. + +### Reader max array elements + +By default the hiredis reply parser sets the maximum number of multi-bulk elements +to 2^32 - 1 or 4,294,967,295 entries. If you need to process multi-bulk replies +with more than this many elements you can set the value higher or to zero, meaning +unlimited with: +```c +context->reader->maxelements = 0; +``` + +## SSL/TLS Support + +### Building + +SSL/TLS support is not built by default and requires an explicit flag: + + make USE_SSL=1 + +This requires OpenSSL development package (e.g. including header files to be +available. + +When enabled, SSL/TLS support is built into extra `libhiredis_ssl.a` and +`libhiredis_ssl.so` static/dynamic libraries. This leaves the original libraries +unaffected so no additional dependencies are introduced. + +### Using it + +First, you'll need to make sure you include the SSL header file: + +```c +#include "hiredis.h" +#include "hiredis_ssl.h" +``` + +You will also need to link against `libhiredis_ssl`, **in addition** to +`libhiredis` and add `-lssl -lcrypto` to satisfy its dependencies. + +Hiredis implements SSL/TLS on top of its normal `redisContext` or +`redisAsyncContext`, so you will need to establish a connection first and then +initiate an SSL/TLS handshake. + +#### Hiredis OpenSSL Wrappers + +Before Hiredis can negotiate an SSL/TLS connection, it is necessary to +initialize OpenSSL and create a context. You can do that in two ways: + +1. Work directly with the OpenSSL API to initialize the library's global context + and create `SSL_CTX *` and `SSL *` contexts. With an `SSL *` object you can + call `redisInitiateSSL()`. +2. Work with a set of Hiredis-provided wrappers around OpenSSL, create a + `redisSSLContext` object to hold configuration and use + `redisInitiateSSLWithContext()` to initiate the SSL/TLS handshake. + +```c +/* An Hiredis SSL context. It holds SSL configuration and can be reused across + * many contexts. + */ +redisSSLContext *ssl; + +/* An error variable to indicate what went wrong, if the context fails to + * initialize. + */ +redisSSLContextError ssl_error; + +/* Initialize global OpenSSL state. + * + * You should call this only once when your app initializes, and only if + * you don't explicitly or implicitly initialize OpenSSL it elsewhere. + */ +redisInitOpenSSL(); + +/* Create SSL context */ +ssl = redisCreateSSLContext( + "cacertbundle.crt", /* File name of trusted CA/ca bundle file, optional */ + "/path/to/certs", /* Path of trusted certificates, optional */ + "client_cert.pem", /* File name of client certificate file, optional */ + "client_key.pem", /* File name of client private key, optional */ + "redis.mydomain.com", /* Server name to request (SNI), optional */ + &ssl_error + ) != REDIS_OK) { + printf("SSL error: %s\n", redisSSLContextGetError(ssl_error); + /* Abort... */ + } + +/* Create Redis context and establish connection */ +c = redisConnect("localhost", 6443); +if (c == NULL || c->err) { + /* Handle error and abort... */ +} + +/* Negotiate SSL/TLS */ +if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) { + /* Handle error, in c->err / c->errstr */ +} +``` + +## RESP3 PUSH replies +Redis 6.0 introduced PUSH replies with the reply-type `>`. These messages are generated spontaneously and can arrive at any time, so must be handled using callbacks. + +### Default behavior +Hiredis installs handlers on `redisContext` and `redisAsyncContext` by default, which will intercept and free any PUSH replies detected. This means existing code will work as-is after upgrading to Redis 6 and switching to `RESP3`. + +### Custom PUSH handler prototypes +The callback prototypes differ between `redisContext` and `redisAsyncContext`. + +#### redisContext +```c +void my_push_handler(void *privdata, void *reply) { + /* Handle the reply */ + + /* Note: We need to free the reply in our custom handler for + blocking contexts. This lets us keep the reply if + we want. */ + freeReplyObject(reply); +} +``` + +#### redisAsyncContext +```c +void my_async_push_handler(redisAsyncContext *ac, void *reply) { + /* Handle the reply */ + + /* Note: Because async hiredis always frees replies, you should + not call freeReplyObject in an async push callback. */ +} +``` + +### Installing a custom handler +There are two ways to set your own PUSH handlers. + +1. Set `push_cb` or `async_push_cb` in the `redisOptions` struct and connect with `redisConnectWithOptions` or `redisAsyncConnectWithOptions`. + ```c + redisOptions = {0}; + REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379); + options->push_cb = my_push_handler; + redisContext *context = redisConnectWithOptions(&options); + ``` +2. Call `redisSetPushCallback` or `redisAsyncSetPushCallback` on a connected context. + ```c + redisContext *context = redisConnect("127.0.0.1", 6379); + redisSetPushCallback(context, my_push_handler); + ``` + + _Note `redisSetPushCallback` and `redisAsyncSetPushCallback` both return any currently configured handler, making it easy to override and then return to the old value._ + +### Specifying no handler +If you have a unique use-case where you don't want hiredis to automatically intercept and free PUSH replies, you will want to configure no handler at all. This can be done in two ways. +1. Set the `REDIS_OPT_NO_PUSH_AUTOFREE` flag in `redisOptions` and leave the callback function pointer `NULL`. + ```c + redisOptions = {0}; + REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379); + options->options |= REDIS_OPT_NO_PUSH_AUTOFREE; + redisContext *context = redisConnectWithOptions(&options); + ``` +3. Call `redisSetPushCallback` with `NULL` once connected. + ```c + redisContext *context = redisConnect("127.0.0.1", 6379); + redisSetPushCallback(context, NULL); + ``` + + _Note: With no handler configured, calls to `redisCommand` may generate more than one reply, so this strategy is only applicable when there's some kind of blocking`redisGetReply()` loop (e.g. `MONITOR` or `SUBSCRIBE` workloads)._ + +## Allocator injection + +Hiredis uses a pass-thru structure of function pointers defined in [alloc.h](https://github.com/redis/hiredis/blob/f5d25850/alloc.h#L41) that contain the currently configured allocation and deallocation functions. By default they just point to libc (`malloc`, `calloc`, `realloc`, etc). + +### Overriding + +One can override the allocators like so: + +```c +hiredisAllocFuncs myfuncs = { + .mallocFn = my_malloc, + .callocFn = my_calloc, + .reallocFn = my_realloc, + .strdupFn = my_strdup, + .freeFn = my_free, +}; + +// Override allocators (function returns current allocators if needed) +hiredisAllocFuncs orig = hiredisSetAllocators(&myfuncs); +``` + +To reset the allocators to their default libc function simply call: + +```c +hiredisResetAllocators(); +``` + +## AUTHORS + +Salvatore Sanfilippo (antirez at gmail),\ +Pieter Noordhuis (pcnoordhuis at gmail)\ +Michael Grunder (michael dot grunder at gmail) + +_Hiredis is released under the BSD license._ diff --git a/ext/hiredis-1.0.2/adapters/ae.h b/ext/hiredis-1.0.2/adapters/ae.h new file mode 100644 index 000000000..660d82eb0 --- /dev/null +++ b/ext/hiredis-1.0.2/adapters/ae.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_AE_H__ +#define __HIREDIS_AE_H__ +#include +#include +#include "../hiredis.h" +#include "../async.h" + +typedef struct redisAeEvents { + redisAsyncContext *context; + aeEventLoop *loop; + int fd; + int reading, writing; +} redisAeEvents; + +static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) { + ((void)el); ((void)fd); ((void)mask); + + redisAeEvents *e = (redisAeEvents*)privdata; + redisAsyncHandleRead(e->context); +} + +static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) { + ((void)el); ((void)fd); ((void)mask); + + redisAeEvents *e = (redisAeEvents*)privdata; + redisAsyncHandleWrite(e->context); +} + +static void redisAeAddRead(void *privdata) { + redisAeEvents *e = (redisAeEvents*)privdata; + aeEventLoop *loop = e->loop; + if (!e->reading) { + e->reading = 1; + aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e); + } +} + +static void redisAeDelRead(void *privdata) { + redisAeEvents *e = (redisAeEvents*)privdata; + aeEventLoop *loop = e->loop; + if (e->reading) { + e->reading = 0; + aeDeleteFileEvent(loop,e->fd,AE_READABLE); + } +} + +static void redisAeAddWrite(void *privdata) { + redisAeEvents *e = (redisAeEvents*)privdata; + aeEventLoop *loop = e->loop; + if (!e->writing) { + e->writing = 1; + aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e); + } +} + +static void redisAeDelWrite(void *privdata) { + redisAeEvents *e = (redisAeEvents*)privdata; + aeEventLoop *loop = e->loop; + if (e->writing) { + e->writing = 0; + aeDeleteFileEvent(loop,e->fd,AE_WRITABLE); + } +} + +static void redisAeCleanup(void *privdata) { + redisAeEvents *e = (redisAeEvents*)privdata; + redisAeDelRead(privdata); + redisAeDelWrite(privdata); + hi_free(e); +} + +static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) { + redisContext *c = &(ac->c); + redisAeEvents *e; + + /* Nothing should be attached when something is already attached */ + if (ac->ev.data != NULL) + return REDIS_ERR; + + /* Create container for context and r/w events */ + e = (redisAeEvents*)hi_malloc(sizeof(*e)); + if (e == NULL) + return REDIS_ERR; + + e->context = ac; + e->loop = loop; + e->fd = c->fd; + e->reading = e->writing = 0; + + /* Register functions to start/stop listening for events */ + ac->ev.addRead = redisAeAddRead; + ac->ev.delRead = redisAeDelRead; + ac->ev.addWrite = redisAeAddWrite; + ac->ev.delWrite = redisAeDelWrite; + ac->ev.cleanup = redisAeCleanup; + ac->ev.data = e; + + return REDIS_OK; +} +#endif diff --git a/ext/hiredis-1.0.2/adapters/glib.h b/ext/hiredis-1.0.2/adapters/glib.h new file mode 100644 index 000000000..ad59dd142 --- /dev/null +++ b/ext/hiredis-1.0.2/adapters/glib.h @@ -0,0 +1,156 @@ +#ifndef __HIREDIS_GLIB_H__ +#define __HIREDIS_GLIB_H__ + +#include + +#include "../hiredis.h" +#include "../async.h" + +typedef struct +{ + GSource source; + redisAsyncContext *ac; + GPollFD poll_fd; +} RedisSource; + +static void +redis_source_add_read (gpointer data) +{ + RedisSource *source = (RedisSource *)data; + g_return_if_fail(source); + source->poll_fd.events |= G_IO_IN; + g_main_context_wakeup(g_source_get_context((GSource *)data)); +} + +static void +redis_source_del_read (gpointer data) +{ + RedisSource *source = (RedisSource *)data; + g_return_if_fail(source); + source->poll_fd.events &= ~G_IO_IN; + g_main_context_wakeup(g_source_get_context((GSource *)data)); +} + +static void +redis_source_add_write (gpointer data) +{ + RedisSource *source = (RedisSource *)data; + g_return_if_fail(source); + source->poll_fd.events |= G_IO_OUT; + g_main_context_wakeup(g_source_get_context((GSource *)data)); +} + +static void +redis_source_del_write (gpointer data) +{ + RedisSource *source = (RedisSource *)data; + g_return_if_fail(source); + source->poll_fd.events &= ~G_IO_OUT; + g_main_context_wakeup(g_source_get_context((GSource *)data)); +} + +static void +redis_source_cleanup (gpointer data) +{ + RedisSource *source = (RedisSource *)data; + + g_return_if_fail(source); + + redis_source_del_read(source); + redis_source_del_write(source); + /* + * It is not our responsibility to remove ourself from the + * current main loop. However, we will remove the GPollFD. + */ + if (source->poll_fd.fd >= 0) { + g_source_remove_poll((GSource *)data, &source->poll_fd); + source->poll_fd.fd = -1; + } +} + +static gboolean +redis_source_prepare (GSource *source, + gint *timeout_) +{ + RedisSource *redis = (RedisSource *)source; + *timeout_ = -1; + return !!(redis->poll_fd.events & redis->poll_fd.revents); +} + +static gboolean +redis_source_check (GSource *source) +{ + RedisSource *redis = (RedisSource *)source; + return !!(redis->poll_fd.events & redis->poll_fd.revents); +} + +static gboolean +redis_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + RedisSource *redis = (RedisSource *)source; + + if ((redis->poll_fd.revents & G_IO_OUT)) { + redisAsyncHandleWrite(redis->ac); + redis->poll_fd.revents &= ~G_IO_OUT; + } + + if ((redis->poll_fd.revents & G_IO_IN)) { + redisAsyncHandleRead(redis->ac); + redis->poll_fd.revents &= ~G_IO_IN; + } + + if (callback) { + return callback(user_data); + } + + return TRUE; +} + +static void +redis_source_finalize (GSource *source) +{ + RedisSource *redis = (RedisSource *)source; + + if (redis->poll_fd.fd >= 0) { + g_source_remove_poll(source, &redis->poll_fd); + redis->poll_fd.fd = -1; + } +} + +static GSource * +redis_source_new (redisAsyncContext *ac) +{ + static GSourceFuncs source_funcs = { + .prepare = redis_source_prepare, + .check = redis_source_check, + .dispatch = redis_source_dispatch, + .finalize = redis_source_finalize, + }; + redisContext *c = &ac->c; + RedisSource *source; + + g_return_val_if_fail(ac != NULL, NULL); + + source = (RedisSource *)g_source_new(&source_funcs, sizeof *source); + if (source == NULL) + return NULL; + + source->ac = ac; + source->poll_fd.fd = c->fd; + source->poll_fd.events = 0; + source->poll_fd.revents = 0; + g_source_add_poll((GSource *)source, &source->poll_fd); + + ac->ev.addRead = redis_source_add_read; + ac->ev.delRead = redis_source_del_read; + ac->ev.addWrite = redis_source_add_write; + ac->ev.delWrite = redis_source_del_write; + ac->ev.cleanup = redis_source_cleanup; + ac->ev.data = source; + + return (GSource *)source; +} + +#endif /* __HIREDIS_GLIB_H__ */ diff --git a/ext/hiredis-1.0.2/adapters/ivykis.h b/ext/hiredis-1.0.2/adapters/ivykis.h new file mode 100644 index 000000000..179f6ab52 --- /dev/null +++ b/ext/hiredis-1.0.2/adapters/ivykis.h @@ -0,0 +1,84 @@ +#ifndef __HIREDIS_IVYKIS_H__ +#define __HIREDIS_IVYKIS_H__ +#include +#include "../hiredis.h" +#include "../async.h" + +typedef struct redisIvykisEvents { + redisAsyncContext *context; + struct iv_fd fd; +} redisIvykisEvents; + +static void redisIvykisReadEvent(void *arg) { + redisAsyncContext *context = (redisAsyncContext *)arg; + redisAsyncHandleRead(context); +} + +static void redisIvykisWriteEvent(void *arg) { + redisAsyncContext *context = (redisAsyncContext *)arg; + redisAsyncHandleWrite(context); +} + +static void redisIvykisAddRead(void *privdata) { + redisIvykisEvents *e = (redisIvykisEvents*)privdata; + iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent); +} + +static void redisIvykisDelRead(void *privdata) { + redisIvykisEvents *e = (redisIvykisEvents*)privdata; + iv_fd_set_handler_in(&e->fd, NULL); +} + +static void redisIvykisAddWrite(void *privdata) { + redisIvykisEvents *e = (redisIvykisEvents*)privdata; + iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent); +} + +static void redisIvykisDelWrite(void *privdata) { + redisIvykisEvents *e = (redisIvykisEvents*)privdata; + iv_fd_set_handler_out(&e->fd, NULL); +} + +static void redisIvykisCleanup(void *privdata) { + redisIvykisEvents *e = (redisIvykisEvents*)privdata; + + iv_fd_unregister(&e->fd); + hi_free(e); +} + +static int redisIvykisAttach(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + redisIvykisEvents *e; + + /* Nothing should be attached when something is already attached */ + if (ac->ev.data != NULL) + return REDIS_ERR; + + /* Create container for context and r/w events */ + e = (redisIvykisEvents*)hi_malloc(sizeof(*e)); + if (e == NULL) + return REDIS_ERR; + + e->context = ac; + + /* Register functions to start/stop listening for events */ + ac->ev.addRead = redisIvykisAddRead; + ac->ev.delRead = redisIvykisDelRead; + ac->ev.addWrite = redisIvykisAddWrite; + ac->ev.delWrite = redisIvykisDelWrite; + ac->ev.cleanup = redisIvykisCleanup; + ac->ev.data = e; + + /* Initialize and install read/write events */ + IV_FD_INIT(&e->fd); + e->fd.fd = c->fd; + e->fd.handler_in = redisIvykisReadEvent; + e->fd.handler_out = redisIvykisWriteEvent; + e->fd.handler_err = NULL; + e->fd.cookie = e->context; + + iv_fd_register(&e->fd); + + return REDIS_OK; +} +#endif diff --git a/ext/hiredis-1.0.2/adapters/libev.h b/ext/hiredis-1.0.2/adapters/libev.h new file mode 100644 index 000000000..e1e7bbd99 --- /dev/null +++ b/ext/hiredis-1.0.2/adapters/libev.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_LIBEV_H__ +#define __HIREDIS_LIBEV_H__ +#include +#include +#include +#include "../hiredis.h" +#include "../async.h" + +typedef struct redisLibevEvents { + redisAsyncContext *context; + struct ev_loop *loop; + int reading, writing; + ev_io rev, wev; + ev_timer timer; +} redisLibevEvents; + +static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) { +#if EV_MULTIPLICITY + ((void)loop); +#endif + ((void)revents); + + redisLibevEvents *e = (redisLibevEvents*)watcher->data; + redisAsyncHandleRead(e->context); +} + +static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) { +#if EV_MULTIPLICITY + ((void)loop); +#endif + ((void)revents); + + redisLibevEvents *e = (redisLibevEvents*)watcher->data; + redisAsyncHandleWrite(e->context); +} + +static void redisLibevAddRead(void *privdata) { + redisLibevEvents *e = (redisLibevEvents*)privdata; + struct ev_loop *loop = e->loop; + ((void)loop); + if (!e->reading) { + e->reading = 1; + ev_io_start(EV_A_ &e->rev); + } +} + +static void redisLibevDelRead(void *privdata) { + redisLibevEvents *e = (redisLibevEvents*)privdata; + struct ev_loop *loop = e->loop; + ((void)loop); + if (e->reading) { + e->reading = 0; + ev_io_stop(EV_A_ &e->rev); + } +} + +static void redisLibevAddWrite(void *privdata) { + redisLibevEvents *e = (redisLibevEvents*)privdata; + struct ev_loop *loop = e->loop; + ((void)loop); + if (!e->writing) { + e->writing = 1; + ev_io_start(EV_A_ &e->wev); + } +} + +static void redisLibevDelWrite(void *privdata) { + redisLibevEvents *e = (redisLibevEvents*)privdata; + struct ev_loop *loop = e->loop; + ((void)loop); + if (e->writing) { + e->writing = 0; + ev_io_stop(EV_A_ &e->wev); + } +} + +static void redisLibevStopTimer(void *privdata) { + redisLibevEvents *e = (redisLibevEvents*)privdata; + struct ev_loop *loop = e->loop; + ((void)loop); + ev_timer_stop(EV_A_ &e->timer); +} + +static void redisLibevCleanup(void *privdata) { + redisLibevEvents *e = (redisLibevEvents*)privdata; + redisLibevDelRead(privdata); + redisLibevDelWrite(privdata); + redisLibevStopTimer(privdata); + hi_free(e); +} + +static void redisLibevTimeout(EV_P_ ev_timer *timer, int revents) { + ((void)revents); + redisLibevEvents *e = (redisLibevEvents*)timer->data; + redisAsyncHandleTimeout(e->context); +} + +static void redisLibevSetTimeout(void *privdata, struct timeval tv) { + redisLibevEvents *e = (redisLibevEvents*)privdata; + struct ev_loop *loop = e->loop; + ((void)loop); + + if (!ev_is_active(&e->timer)) { + ev_init(&e->timer, redisLibevTimeout); + e->timer.data = e; + } + + e->timer.repeat = tv.tv_sec + tv.tv_usec / 1000000.00; + ev_timer_again(EV_A_ &e->timer); +} + +static int redisLibevAttach(EV_P_ redisAsyncContext *ac) { + redisContext *c = &(ac->c); + redisLibevEvents *e; + + /* Nothing should be attached when something is already attached */ + if (ac->ev.data != NULL) + return REDIS_ERR; + + /* Create container for context and r/w events */ + e = (redisLibevEvents*)hi_calloc(1, sizeof(*e)); + if (e == NULL) + return REDIS_ERR; + + e->context = ac; +#if EV_MULTIPLICITY + e->loop = loop; +#else + e->loop = NULL; +#endif + e->rev.data = e; + e->wev.data = e; + + /* Register functions to start/stop listening for events */ + ac->ev.addRead = redisLibevAddRead; + ac->ev.delRead = redisLibevDelRead; + ac->ev.addWrite = redisLibevAddWrite; + ac->ev.delWrite = redisLibevDelWrite; + ac->ev.cleanup = redisLibevCleanup; + ac->ev.scheduleTimer = redisLibevSetTimeout; + ac->ev.data = e; + + /* Initialize read/write events */ + ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ); + ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE); + return REDIS_OK; +} + +#endif diff --git a/ext/hiredis-1.0.2/adapters/libevent.h b/ext/hiredis-1.0.2/adapters/libevent.h new file mode 100644 index 000000000..9150979bc --- /dev/null +++ b/ext/hiredis-1.0.2/adapters/libevent.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_LIBEVENT_H__ +#define __HIREDIS_LIBEVENT_H__ +#include +#include "../hiredis.h" +#include "../async.h" + +#define REDIS_LIBEVENT_DELETED 0x01 +#define REDIS_LIBEVENT_ENTERED 0x02 + +typedef struct redisLibeventEvents { + redisAsyncContext *context; + struct event *ev; + struct event_base *base; + struct timeval tv; + short flags; + short state; +} redisLibeventEvents; + +static void redisLibeventDestroy(redisLibeventEvents *e) { + hi_free(e); +} + +static void redisLibeventHandler(int fd, short event, void *arg) { + ((void)fd); + redisLibeventEvents *e = (redisLibeventEvents*)arg; + e->state |= REDIS_LIBEVENT_ENTERED; + + #define CHECK_DELETED() if (e->state & REDIS_LIBEVENT_DELETED) {\ + redisLibeventDestroy(e);\ + return; \ + } + + if ((event & EV_TIMEOUT) && (e->state & REDIS_LIBEVENT_DELETED) == 0) { + redisAsyncHandleTimeout(e->context); + CHECK_DELETED(); + } + + if ((event & EV_READ) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) { + redisAsyncHandleRead(e->context); + CHECK_DELETED(); + } + + if ((event & EV_WRITE) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) { + redisAsyncHandleWrite(e->context); + CHECK_DELETED(); + } + + e->state &= ~REDIS_LIBEVENT_ENTERED; + #undef CHECK_DELETED +} + +static void redisLibeventUpdate(void *privdata, short flag, int isRemove) { + redisLibeventEvents *e = (redisLibeventEvents *)privdata; + const struct timeval *tv = e->tv.tv_sec || e->tv.tv_usec ? &e->tv : NULL; + + if (isRemove) { + if ((e->flags & flag) == 0) { + return; + } else { + e->flags &= ~flag; + } + } else { + if (e->flags & flag) { + return; + } else { + e->flags |= flag; + } + } + + event_del(e->ev); + event_assign(e->ev, e->base, e->context->c.fd, e->flags | EV_PERSIST, + redisLibeventHandler, privdata); + event_add(e->ev, tv); +} + +static void redisLibeventAddRead(void *privdata) { + redisLibeventUpdate(privdata, EV_READ, 0); +} + +static void redisLibeventDelRead(void *privdata) { + redisLibeventUpdate(privdata, EV_READ, 1); +} + +static void redisLibeventAddWrite(void *privdata) { + redisLibeventUpdate(privdata, EV_WRITE, 0); +} + +static void redisLibeventDelWrite(void *privdata) { + redisLibeventUpdate(privdata, EV_WRITE, 1); +} + +static void redisLibeventCleanup(void *privdata) { + redisLibeventEvents *e = (redisLibeventEvents*)privdata; + if (!e) { + return; + } + event_del(e->ev); + event_free(e->ev); + e->ev = NULL; + + if (e->state & REDIS_LIBEVENT_ENTERED) { + e->state |= REDIS_LIBEVENT_DELETED; + } else { + redisLibeventDestroy(e); + } +} + +static void redisLibeventSetTimeout(void *privdata, struct timeval tv) { + redisLibeventEvents *e = (redisLibeventEvents *)privdata; + short flags = e->flags; + e->flags = 0; + e->tv = tv; + redisLibeventUpdate(e, flags, 0); +} + +static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) { + redisContext *c = &(ac->c); + redisLibeventEvents *e; + + /* Nothing should be attached when something is already attached */ + if (ac->ev.data != NULL) + return REDIS_ERR; + + /* Create container for context and r/w events */ + e = (redisLibeventEvents*)hi_calloc(1, sizeof(*e)); + if (e == NULL) + return REDIS_ERR; + + e->context = ac; + + /* Register functions to start/stop listening for events */ + ac->ev.addRead = redisLibeventAddRead; + ac->ev.delRead = redisLibeventDelRead; + ac->ev.addWrite = redisLibeventAddWrite; + ac->ev.delWrite = redisLibeventDelWrite; + ac->ev.cleanup = redisLibeventCleanup; + ac->ev.scheduleTimer = redisLibeventSetTimeout; + ac->ev.data = e; + + /* Initialize and install read/write events */ + e->ev = event_new(base, c->fd, EV_READ | EV_WRITE, redisLibeventHandler, e); + e->base = base; + return REDIS_OK; +} +#endif diff --git a/ext/hiredis-1.0.2/adapters/libuv.h b/ext/hiredis-1.0.2/adapters/libuv.h new file mode 100644 index 000000000..c120b1b39 --- /dev/null +++ b/ext/hiredis-1.0.2/adapters/libuv.h @@ -0,0 +1,117 @@ +#ifndef __HIREDIS_LIBUV_H__ +#define __HIREDIS_LIBUV_H__ +#include +#include +#include "../hiredis.h" +#include "../async.h" +#include + +typedef struct redisLibuvEvents { + redisAsyncContext* context; + uv_poll_t handle; + int events; +} redisLibuvEvents; + + +static void redisLibuvPoll(uv_poll_t* handle, int status, int events) { + redisLibuvEvents* p = (redisLibuvEvents*)handle->data; + int ev = (status ? p->events : events); + + if (p->context != NULL && (ev & UV_READABLE)) { + redisAsyncHandleRead(p->context); + } + if (p->context != NULL && (ev & UV_WRITABLE)) { + redisAsyncHandleWrite(p->context); + } +} + + +static void redisLibuvAddRead(void *privdata) { + redisLibuvEvents* p = (redisLibuvEvents*)privdata; + + p->events |= UV_READABLE; + + uv_poll_start(&p->handle, p->events, redisLibuvPoll); +} + + +static void redisLibuvDelRead(void *privdata) { + redisLibuvEvents* p = (redisLibuvEvents*)privdata; + + p->events &= ~UV_READABLE; + + if (p->events) { + uv_poll_start(&p->handle, p->events, redisLibuvPoll); + } else { + uv_poll_stop(&p->handle); + } +} + + +static void redisLibuvAddWrite(void *privdata) { + redisLibuvEvents* p = (redisLibuvEvents*)privdata; + + p->events |= UV_WRITABLE; + + uv_poll_start(&p->handle, p->events, redisLibuvPoll); +} + + +static void redisLibuvDelWrite(void *privdata) { + redisLibuvEvents* p = (redisLibuvEvents*)privdata; + + p->events &= ~UV_WRITABLE; + + if (p->events) { + uv_poll_start(&p->handle, p->events, redisLibuvPoll); + } else { + uv_poll_stop(&p->handle); + } +} + + +static void on_close(uv_handle_t* handle) { + redisLibuvEvents* p = (redisLibuvEvents*)handle->data; + + hi_free(p); +} + + +static void redisLibuvCleanup(void *privdata) { + redisLibuvEvents* p = (redisLibuvEvents*)privdata; + + p->context = NULL; // indicate that context might no longer exist + uv_close((uv_handle_t*)&p->handle, on_close); +} + + +static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) { + redisContext *c = &(ac->c); + + if (ac->ev.data != NULL) { + return REDIS_ERR; + } + + ac->ev.addRead = redisLibuvAddRead; + ac->ev.delRead = redisLibuvDelRead; + ac->ev.addWrite = redisLibuvAddWrite; + ac->ev.delWrite = redisLibuvDelWrite; + ac->ev.cleanup = redisLibuvCleanup; + + redisLibuvEvents* p = (redisLibuvEvents*)hi_malloc(sizeof(*p)); + if (p == NULL) + return REDIS_ERR; + + memset(p, 0, sizeof(*p)); + + if (uv_poll_init_socket(loop, &p->handle, c->fd) != 0) { + return REDIS_ERR; + } + + ac->ev.data = p; + p->handle.data = p; + p->context = ac; + + return REDIS_OK; +} +#endif diff --git a/ext/hiredis-1.0.2/adapters/macosx.h b/ext/hiredis-1.0.2/adapters/macosx.h new file mode 100644 index 000000000..3c87f1b2f --- /dev/null +++ b/ext/hiredis-1.0.2/adapters/macosx.h @@ -0,0 +1,115 @@ +// +// Created by Дмитрий Бахвалов on 13.07.15. +// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved. +// + +#ifndef __HIREDIS_MACOSX_H__ +#define __HIREDIS_MACOSX_H__ + +#include + +#include "../hiredis.h" +#include "../async.h" + +typedef struct { + redisAsyncContext *context; + CFSocketRef socketRef; + CFRunLoopSourceRef sourceRef; +} RedisRunLoop; + +static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) { + if( redisRunLoop != NULL ) { + if( redisRunLoop->sourceRef != NULL ) { + CFRunLoopSourceInvalidate(redisRunLoop->sourceRef); + CFRelease(redisRunLoop->sourceRef); + } + if( redisRunLoop->socketRef != NULL ) { + CFSocketInvalidate(redisRunLoop->socketRef); + CFRelease(redisRunLoop->socketRef); + } + hi_free(redisRunLoop); + } + return REDIS_ERR; +} + +static void redisMacOSAddRead(void *privdata) { + RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; + CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); +} + +static void redisMacOSDelRead(void *privdata) { + RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; + CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); +} + +static void redisMacOSAddWrite(void *privdata) { + RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; + CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); +} + +static void redisMacOSDelWrite(void *privdata) { + RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; + CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); +} + +static void redisMacOSCleanup(void *privdata) { + RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; + freeRedisRunLoop(redisRunLoop); +} + +static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) { + redisAsyncContext* context = (redisAsyncContext*) info; + + switch (callbackType) { + case kCFSocketReadCallBack: + redisAsyncHandleRead(context); + break; + + case kCFSocketWriteCallBack: + redisAsyncHandleWrite(context); + break; + + default: + break; + } +} + +static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) { + redisContext *redisCtx = &(redisAsyncCtx->c); + + /* Nothing should be attached when something is already attached */ + if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR; + + RedisRunLoop* redisRunLoop = (RedisRunLoop*) hi_calloc(1, sizeof(RedisRunLoop)); + if (redisRunLoop == NULL) + return REDIS_ERR; + + /* Setup redis stuff */ + redisRunLoop->context = redisAsyncCtx; + + redisAsyncCtx->ev.addRead = redisMacOSAddRead; + redisAsyncCtx->ev.delRead = redisMacOSDelRead; + redisAsyncCtx->ev.addWrite = redisMacOSAddWrite; + redisAsyncCtx->ev.delWrite = redisMacOSDelWrite; + redisAsyncCtx->ev.cleanup = redisMacOSCleanup; + redisAsyncCtx->ev.data = redisRunLoop; + + /* Initialize and install read/write events */ + CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL }; + + redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd, + kCFSocketReadCallBack | kCFSocketWriteCallBack, + redisMacOSAsyncCallback, + &socketCtx); + if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop); + + redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0); + if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop); + + CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode); + + return REDIS_OK; +} + +#endif + diff --git a/ext/hiredis-1.0.2/adapters/qt.h b/ext/hiredis-1.0.2/adapters/qt.h new file mode 100644 index 000000000..5cc02e6ce --- /dev/null +++ b/ext/hiredis-1.0.2/adapters/qt.h @@ -0,0 +1,135 @@ +/*- + * Copyright (C) 2014 Pietro Cerutti + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __HIREDIS_QT_H__ +#define __HIREDIS_QT_H__ +#include +#include "../async.h" + +static void RedisQtAddRead(void *); +static void RedisQtDelRead(void *); +static void RedisQtAddWrite(void *); +static void RedisQtDelWrite(void *); +static void RedisQtCleanup(void *); + +class RedisQtAdapter : public QObject { + + Q_OBJECT + + friend + void RedisQtAddRead(void * adapter) { + RedisQtAdapter * a = static_cast(adapter); + a->addRead(); + } + + friend + void RedisQtDelRead(void * adapter) { + RedisQtAdapter * a = static_cast(adapter); + a->delRead(); + } + + friend + void RedisQtAddWrite(void * adapter) { + RedisQtAdapter * a = static_cast(adapter); + a->addWrite(); + } + + friend + void RedisQtDelWrite(void * adapter) { + RedisQtAdapter * a = static_cast(adapter); + a->delWrite(); + } + + friend + void RedisQtCleanup(void * adapter) { + RedisQtAdapter * a = static_cast(adapter); + a->cleanup(); + } + + public: + RedisQtAdapter(QObject * parent = 0) + : QObject(parent), m_ctx(0), m_read(0), m_write(0) { } + + ~RedisQtAdapter() { + if (m_ctx != 0) { + m_ctx->ev.data = NULL; + } + } + + int setContext(redisAsyncContext * ac) { + if (ac->ev.data != NULL) { + return REDIS_ERR; + } + m_ctx = ac; + m_ctx->ev.data = this; + m_ctx->ev.addRead = RedisQtAddRead; + m_ctx->ev.delRead = RedisQtDelRead; + m_ctx->ev.addWrite = RedisQtAddWrite; + m_ctx->ev.delWrite = RedisQtDelWrite; + m_ctx->ev.cleanup = RedisQtCleanup; + return REDIS_OK; + } + + private: + void addRead() { + if (m_read) return; + m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0); + connect(m_read, SIGNAL(activated(int)), this, SLOT(read())); + } + + void delRead() { + if (!m_read) return; + delete m_read; + m_read = 0; + } + + void addWrite() { + if (m_write) return; + m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0); + connect(m_write, SIGNAL(activated(int)), this, SLOT(write())); + } + + void delWrite() { + if (!m_write) return; + delete m_write; + m_write = 0; + } + + void cleanup() { + delRead(); + delWrite(); + } + + private slots: + void read() { redisAsyncHandleRead(m_ctx); } + void write() { redisAsyncHandleWrite(m_ctx); } + + private: + redisAsyncContext * m_ctx; + QSocketNotifier * m_read; + QSocketNotifier * m_write; +}; + +#endif /* !__HIREDIS_QT_H__ */ diff --git a/ext/hiredis-1.0.2/alloc.c b/ext/hiredis-1.0.2/alloc.c new file mode 100644 index 000000000..7fb6b35e7 --- /dev/null +++ b/ext/hiredis-1.0.2/alloc.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2020, Michael Grunder + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include "alloc.h" +#include +#include + +hiredisAllocFuncs hiredisAllocFns = { + .mallocFn = malloc, + .callocFn = calloc, + .reallocFn = realloc, + .strdupFn = strdup, + .freeFn = free, +}; + +/* Override hiredis' allocators with ones supplied by the user */ +hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *override) { + hiredisAllocFuncs orig = hiredisAllocFns; + + hiredisAllocFns = *override; + + return orig; +} + +/* Reset allocators to use libc defaults */ +void hiredisResetAllocators(void) { + hiredisAllocFns = (hiredisAllocFuncs) { + .mallocFn = malloc, + .callocFn = calloc, + .reallocFn = realloc, + .strdupFn = strdup, + .freeFn = free, + }; +} + +#ifdef _WIN32 + +void *hi_malloc(size_t size) { + return hiredisAllocFns.mallocFn(size); +} + +void *hi_calloc(size_t nmemb, size_t size) { + return hiredisAllocFns.callocFn(nmemb, size); +} + +void *hi_realloc(void *ptr, size_t size) { + return hiredisAllocFns.reallocFn(ptr, size); +} + +char *hi_strdup(const char *str) { + return hiredisAllocFns.strdupFn(str); +} + +void hi_free(void *ptr) { + hiredisAllocFns.freeFn(ptr); +} + +#endif diff --git a/ext/hiredis-1.0.2/alloc.h b/ext/hiredis-1.0.2/alloc.h new file mode 100644 index 000000000..34a05f49f --- /dev/null +++ b/ext/hiredis-1.0.2/alloc.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020, Michael Grunder + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HIREDIS_ALLOC_H +#define HIREDIS_ALLOC_H + +#include /* for size_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure pointing to our actually configured allocators */ +typedef struct hiredisAllocFuncs { + void *(*mallocFn)(size_t); + void *(*callocFn)(size_t,size_t); + void *(*reallocFn)(void*,size_t); + char *(*strdupFn)(const char*); + void (*freeFn)(void*); +} hiredisAllocFuncs; + +hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha); +void hiredisResetAllocators(void); + +#ifndef _WIN32 + +/* Hiredis' configured allocator function pointer struct */ +extern hiredisAllocFuncs hiredisAllocFns; + +static inline void *hi_malloc(size_t size) { + return hiredisAllocFns.mallocFn(size); +} + +static inline void *hi_calloc(size_t nmemb, size_t size) { + return hiredisAllocFns.callocFn(nmemb, size); +} + +static inline void *hi_realloc(void *ptr, size_t size) { + return hiredisAllocFns.reallocFn(ptr, size); +} + +static inline char *hi_strdup(const char *str) { + return hiredisAllocFns.strdupFn(str); +} + +static inline void hi_free(void *ptr) { + hiredisAllocFns.freeFn(ptr); +} + +#else + +void *hi_malloc(size_t size); +void *hi_calloc(size_t nmemb, size_t size); +void *hi_realloc(void *ptr, size_t size); +char *hi_strdup(const char *str); +void hi_free(void *ptr); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HIREDIS_ALLOC_H */ diff --git a/ext/hiredis-1.0.2/appveyor.yml b/ext/hiredis-1.0.2/appveyor.yml new file mode 100644 index 000000000..5b43fdbeb --- /dev/null +++ b/ext/hiredis-1.0.2/appveyor.yml @@ -0,0 +1,24 @@ +# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin) +environment: + matrix: + - CYG_BASH: C:\cygwin64\bin\bash + CC: gcc + - CYG_BASH: C:\cygwin\bin\bash + CC: gcc + CFLAGS: -m32 + CXXFLAGS: -m32 + LDFLAGS: -m32 + +clone_depth: 1 + +# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail +init: + - git config --global core.autocrlf input + +# Install needed build dependencies +install: + - '%CYG_BASH% -lc "cygcheck -dc cygwin"' + +build_script: + - 'echo building...' + - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0 + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include "alloc.h" +#include +#include +#ifndef _MSC_VER +#include +#endif +#include +#include +#include +#include "async.h" +#include "net.h" +#include "dict.c" +#include "sds.h" +#include "win32.h" + +#include "async_private.h" + +/* Forward declarations of hiredis.c functions */ +int __redisAppendCommand(redisContext *c, const char *cmd, size_t len); +void __redisSetError(redisContext *c, int type, const char *str); + +/* Functions managing dictionary of callbacks for pub/sub. */ +static unsigned int callbackHash(const void *key) { + return dictGenHashFunction((const unsigned char *)key, + sdslen((const sds)key)); +} + +static void *callbackValDup(void *privdata, const void *src) { + ((void) privdata); + redisCallback *dup; + + dup = hi_malloc(sizeof(*dup)); + if (dup == NULL) + return NULL; + + memcpy(dup,src,sizeof(*dup)); + return dup; +} + +static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) { + int l1, l2; + ((void) privdata); + + l1 = sdslen((const sds)key1); + l2 = sdslen((const sds)key2); + if (l1 != l2) return 0; + return memcmp(key1,key2,l1) == 0; +} + +static void callbackKeyDestructor(void *privdata, void *key) { + ((void) privdata); + sdsfree((sds)key); +} + +static void callbackValDestructor(void *privdata, void *val) { + ((void) privdata); + hi_free(val); +} + +static dictType callbackDict = { + callbackHash, + NULL, + callbackValDup, + callbackKeyCompare, + callbackKeyDestructor, + callbackValDestructor +}; + +static redisAsyncContext *redisAsyncInitialize(redisContext *c) { + redisAsyncContext *ac; + dict *channels = NULL, *patterns = NULL; + + channels = dictCreate(&callbackDict,NULL); + if (channels == NULL) + goto oom; + + patterns = dictCreate(&callbackDict,NULL); + if (patterns == NULL) + goto oom; + + ac = hi_realloc(c,sizeof(redisAsyncContext)); + if (ac == NULL) + goto oom; + + c = &(ac->c); + + /* The regular connect functions will always set the flag REDIS_CONNECTED. + * For the async API, we want to wait until the first write event is + * received up before setting this flag, so reset it here. */ + c->flags &= ~REDIS_CONNECTED; + + ac->err = 0; + ac->errstr = NULL; + ac->data = NULL; + ac->dataCleanup = NULL; + + ac->ev.data = NULL; + ac->ev.addRead = NULL; + ac->ev.delRead = NULL; + ac->ev.addWrite = NULL; + ac->ev.delWrite = NULL; + ac->ev.cleanup = NULL; + ac->ev.scheduleTimer = NULL; + + ac->onConnect = NULL; + ac->onDisconnect = NULL; + + ac->replies.head = NULL; + ac->replies.tail = NULL; + ac->sub.invalid.head = NULL; + ac->sub.invalid.tail = NULL; + ac->sub.channels = channels; + ac->sub.patterns = patterns; + + return ac; +oom: + if (channels) dictRelease(channels); + if (patterns) dictRelease(patterns); + return NULL; +} + +/* We want the error field to be accessible directly instead of requiring + * an indirection to the redisContext struct. */ +static void __redisAsyncCopyError(redisAsyncContext *ac) { + if (!ac) + return; + + redisContext *c = &(ac->c); + ac->err = c->err; + ac->errstr = c->errstr; +} + +redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options) { + redisOptions myOptions = *options; + redisContext *c; + redisAsyncContext *ac; + + /* Clear any erroneously set sync callback and flag that we don't want to + * use freeReplyObject by default. */ + myOptions.push_cb = NULL; + myOptions.options |= REDIS_OPT_NO_PUSH_AUTOFREE; + + myOptions.options |= REDIS_OPT_NONBLOCK; + c = redisConnectWithOptions(&myOptions); + if (c == NULL) { + return NULL; + } + + ac = redisAsyncInitialize(c); + if (ac == NULL) { + redisFree(c); + return NULL; + } + + /* Set any configured async push handler */ + redisAsyncSetPushCallback(ac, myOptions.async_push_cb); + + __redisAsyncCopyError(ac); + return ac; +} + +redisAsyncContext *redisAsyncConnect(const char *ip, int port) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + return redisAsyncConnectWithOptions(&options); +} + +redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, + const char *source_addr) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.endpoint.tcp.source_addr = source_addr; + return redisAsyncConnectWithOptions(&options); +} + +redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, + const char *source_addr) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.options |= REDIS_OPT_REUSEADDR; + options.endpoint.tcp.source_addr = source_addr; + return redisAsyncConnectWithOptions(&options); +} + +redisAsyncContext *redisAsyncConnectUnix(const char *path) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_UNIX(&options, path); + return redisAsyncConnectWithOptions(&options); +} + +int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) { + if (ac->onConnect == NULL) { + ac->onConnect = fn; + + /* The common way to detect an established connection is to wait for + * the first write event to be fired. This assumes the related event + * library functions are already set. */ + _EL_ADD_WRITE(ac); + return REDIS_OK; + } + return REDIS_ERR; +} + +int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) { + if (ac->onDisconnect == NULL) { + ac->onDisconnect = fn; + return REDIS_OK; + } + return REDIS_ERR; +} + +/* Helper functions to push/shift callbacks */ +static int __redisPushCallback(redisCallbackList *list, redisCallback *source) { + redisCallback *cb; + + /* Copy callback from stack to heap */ + cb = hi_malloc(sizeof(*cb)); + if (cb == NULL) + return REDIS_ERR_OOM; + + if (source != NULL) { + memcpy(cb,source,sizeof(*cb)); + cb->next = NULL; + } + + /* Store callback in list */ + if (list->head == NULL) + list->head = cb; + if (list->tail != NULL) + list->tail->next = cb; + list->tail = cb; + return REDIS_OK; +} + +static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) { + redisCallback *cb = list->head; + if (cb != NULL) { + list->head = cb->next; + if (cb == list->tail) + list->tail = NULL; + + /* Copy callback from heap to stack */ + if (target != NULL) + memcpy(target,cb,sizeof(*cb)); + hi_free(cb); + return REDIS_OK; + } + return REDIS_ERR; +} + +static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) { + redisContext *c = &(ac->c); + if (cb->fn != NULL) { + c->flags |= REDIS_IN_CALLBACK; + cb->fn(ac,reply,cb->privdata); + c->flags &= ~REDIS_IN_CALLBACK; + } +} + +static void __redisRunPushCallback(redisAsyncContext *ac, redisReply *reply) { + if (ac->push_cb != NULL) { + ac->c.flags |= REDIS_IN_CALLBACK; + ac->push_cb(ac, reply); + ac->c.flags &= ~REDIS_IN_CALLBACK; + } +} + +/* Helper function to free the context. */ +static void __redisAsyncFree(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + redisCallback cb; + dictIterator *it; + dictEntry *de; + + /* Execute pending callbacks with NULL reply. */ + while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK) + __redisRunCallback(ac,&cb,NULL); + + /* Execute callbacks for invalid commands */ + while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK) + __redisRunCallback(ac,&cb,NULL); + + /* Run subscription callbacks with NULL reply */ + if (ac->sub.channels) { + it = dictGetIterator(ac->sub.channels); + if (it != NULL) { + while ((de = dictNext(it)) != NULL) + __redisRunCallback(ac,dictGetEntryVal(de),NULL); + dictReleaseIterator(it); + } + + dictRelease(ac->sub.channels); + } + + if (ac->sub.patterns) { + it = dictGetIterator(ac->sub.patterns); + if (it != NULL) { + while ((de = dictNext(it)) != NULL) + __redisRunCallback(ac,dictGetEntryVal(de),NULL); + dictReleaseIterator(it); + } + + dictRelease(ac->sub.patterns); + } + + /* Signal event lib to clean up */ + _EL_CLEANUP(ac); + + /* Execute disconnect callback. When redisAsyncFree() initiated destroying + * this context, the status will always be REDIS_OK. */ + if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) { + if (c->flags & REDIS_FREEING) { + ac->onDisconnect(ac,REDIS_OK); + } else { + ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR); + } + } + + if (ac->dataCleanup) { + ac->dataCleanup(ac->data); + } + + /* Cleanup self */ + redisFree(c); +} + +/* Free the async context. When this function is called from a callback, + * control needs to be returned to redisProcessCallbacks() before actual + * free'ing. To do so, a flag is set on the context which is picked up by + * redisProcessCallbacks(). Otherwise, the context is immediately free'd. */ +void redisAsyncFree(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + c->flags |= REDIS_FREEING; + if (!(c->flags & REDIS_IN_CALLBACK)) + __redisAsyncFree(ac); +} + +/* Helper function to make the disconnect happen and clean up. */ +void __redisAsyncDisconnect(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + + /* Make sure error is accessible if there is any */ + __redisAsyncCopyError(ac); + + if (ac->err == 0) { + /* For clean disconnects, there should be no pending callbacks. */ + int ret = __redisShiftCallback(&ac->replies,NULL); + assert(ret == REDIS_ERR); + } else { + /* Disconnection is caused by an error, make sure that pending + * callbacks cannot call new commands. */ + c->flags |= REDIS_DISCONNECTING; + } + + /* cleanup event library on disconnect. + * this is safe to call multiple times */ + _EL_CLEANUP(ac); + + /* For non-clean disconnects, __redisAsyncFree() will execute pending + * callbacks with a NULL-reply. */ + if (!(c->flags & REDIS_NO_AUTO_FREE)) { + __redisAsyncFree(ac); + } +} + +/* Tries to do a clean disconnect from Redis, meaning it stops new commands + * from being issued, but tries to flush the output buffer and execute + * callbacks for all remaining replies. When this function is called from a + * callback, there might be more replies and we can safely defer disconnecting + * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately + * when there are no pending callbacks. */ +void redisAsyncDisconnect(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + c->flags |= REDIS_DISCONNECTING; + + /** unset the auto-free flag here, because disconnect undoes this */ + c->flags &= ~REDIS_NO_AUTO_FREE; + if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL) + __redisAsyncDisconnect(ac); +} + +static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) { + redisContext *c = &(ac->c); + dict *callbacks; + redisCallback *cb; + dictEntry *de; + int pvariant; + char *stype; + sds sname; + + /* Custom reply functions are not supported for pub/sub. This will fail + * very hard when they are used... */ + if (reply->type == REDIS_REPLY_ARRAY || reply->type == REDIS_REPLY_PUSH) { + assert(reply->elements >= 2); + assert(reply->element[0]->type == REDIS_REPLY_STRING); + stype = reply->element[0]->str; + pvariant = (tolower(stype[0]) == 'p') ? 1 : 0; + + if (pvariant) + callbacks = ac->sub.patterns; + else + callbacks = ac->sub.channels; + + /* Locate the right callback */ + assert(reply->element[1]->type == REDIS_REPLY_STRING); + sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len); + if (sname == NULL) + goto oom; + + de = dictFind(callbacks,sname); + if (de != NULL) { + cb = dictGetEntryVal(de); + + /* If this is an subscribe reply decrease pending counter. */ + if (strcasecmp(stype+pvariant,"subscribe") == 0) { + cb->pending_subs -= 1; + } + + memcpy(dstcb,cb,sizeof(*dstcb)); + + /* If this is an unsubscribe message, remove it. */ + if (strcasecmp(stype+pvariant,"unsubscribe") == 0) { + if (cb->pending_subs == 0) + dictDelete(callbacks,sname); + + /* If this was the last unsubscribe message, revert to + * non-subscribe mode. */ + assert(reply->element[2]->type == REDIS_REPLY_INTEGER); + + /* Unset subscribed flag only when no pipelined pending subscribe. */ + if (reply->element[2]->integer == 0 + && dictSize(ac->sub.channels) == 0 + && dictSize(ac->sub.patterns) == 0) + c->flags &= ~REDIS_SUBSCRIBED; + } + } + sdsfree(sname); + } else { + /* Shift callback for invalid commands. */ + __redisShiftCallback(&ac->sub.invalid,dstcb); + } + return REDIS_OK; +oom: + __redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; +} + +#define redisIsSpontaneousPushReply(r) \ + (redisIsPushReply(r) && !redisIsSubscribeReply(r)) + +static int redisIsSubscribeReply(redisReply *reply) { + char *str; + size_t len, off; + + /* We will always have at least one string with the subscribe/message type */ + if (reply->elements < 1 || reply->element[0]->type != REDIS_REPLY_STRING || + reply->element[0]->len < sizeof("message") - 1) + { + return 0; + } + + /* Get the string/len moving past 'p' if needed */ + off = tolower(reply->element[0]->str[0]) == 'p'; + str = reply->element[0]->str + off; + len = reply->element[0]->len - off; + + return !strncasecmp(str, "subscribe", len) || + !strncasecmp(str, "message", len); + +} + +void redisProcessCallbacks(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + redisCallback cb = {NULL, NULL, 0, NULL}; + void *reply = NULL; + int status; + + while((status = redisGetReply(c,&reply)) == REDIS_OK) { + if (reply == NULL) { + /* When the connection is being disconnected and there are + * no more replies, this is the cue to really disconnect. */ + if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0 + && ac->replies.head == NULL) { + __redisAsyncDisconnect(ac); + return; + } + + /* If monitor mode, repush callback */ + if(c->flags & REDIS_MONITORING) { + __redisPushCallback(&ac->replies,&cb); + } + + /* When the connection is not being disconnected, simply stop + * trying to get replies and wait for the next loop tick. */ + break; + } + + /* Send any non-subscribe related PUSH messages to our PUSH handler + * while allowing subscribe related PUSH messages to pass through. + * This allows existing code to be backward compatible and work in + * either RESP2 or RESP3 mode. */ + if (redisIsSpontaneousPushReply(reply)) { + __redisRunPushCallback(ac, reply); + c->reader->fn->freeObject(reply); + continue; + } + + /* Even if the context is subscribed, pending regular + * callbacks will get a reply before pub/sub messages arrive. */ + if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) { + /* + * A spontaneous reply in a not-subscribed context can be the error + * reply that is sent when a new connection exceeds the maximum + * number of allowed connections on the server side. + * + * This is seen as an error instead of a regular reply because the + * server closes the connection after sending it. + * + * To prevent the error from being overwritten by an EOF error the + * connection is closed here. See issue #43. + * + * Another possibility is that the server is loading its dataset. + * In this case we also want to close the connection, and have the + * user wait until the server is ready to take our request. + */ + if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) { + c->err = REDIS_ERR_OTHER; + snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str); + c->reader->fn->freeObject(reply); + __redisAsyncDisconnect(ac); + return; + } + /* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */ + assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING)); + if(c->flags & REDIS_SUBSCRIBED) + __redisGetSubscribeCallback(ac,reply,&cb); + } + + if (cb.fn != NULL) { + __redisRunCallback(ac,&cb,reply); + c->reader->fn->freeObject(reply); + + /* Proceed with free'ing when redisAsyncFree() was called. */ + if (c->flags & REDIS_FREEING) { + __redisAsyncFree(ac); + return; + } + } else { + /* No callback for this reply. This can either be a NULL callback, + * or there were no callbacks to begin with. Either way, don't + * abort with an error, but simply ignore it because the client + * doesn't know what the server will spit out over the wire. */ + c->reader->fn->freeObject(reply); + } + } + + /* Disconnect when there was an error reading the reply */ + if (status != REDIS_OK) + __redisAsyncDisconnect(ac); +} + +static void __redisAsyncHandleConnectFailure(redisAsyncContext *ac) { + if (ac->onConnect) ac->onConnect(ac, REDIS_ERR); + __redisAsyncDisconnect(ac); +} + +/* Internal helper function to detect socket status the first time a read or + * write event fires. When connecting was not successful, the connect callback + * is called with a REDIS_ERR status and the context is free'd. */ +static int __redisAsyncHandleConnect(redisAsyncContext *ac) { + int completed = 0; + redisContext *c = &(ac->c); + + if (redisCheckConnectDone(c, &completed) == REDIS_ERR) { + /* Error! */ + redisCheckSocketError(c); + __redisAsyncHandleConnectFailure(ac); + return REDIS_ERR; + } else if (completed == 1) { + /* connected! */ + if (c->connection_type == REDIS_CONN_TCP && + redisSetTcpNoDelay(c) == REDIS_ERR) { + __redisAsyncHandleConnectFailure(ac); + return REDIS_ERR; + } + + if (ac->onConnect) ac->onConnect(ac, REDIS_OK); + c->flags |= REDIS_CONNECTED; + return REDIS_OK; + } else { + return REDIS_OK; + } +} + +void redisAsyncRead(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + + if (redisBufferRead(c) == REDIS_ERR) { + __redisAsyncDisconnect(ac); + } else { + /* Always re-schedule reads */ + _EL_ADD_READ(ac); + redisProcessCallbacks(ac); + } +} + +/* This function should be called when the socket is readable. + * It processes all replies that can be read and executes their callbacks. + */ +void redisAsyncHandleRead(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + + if (!(c->flags & REDIS_CONNECTED)) { + /* Abort connect was not successful. */ + if (__redisAsyncHandleConnect(ac) != REDIS_OK) + return; + /* Try again later when the context is still not connected. */ + if (!(c->flags & REDIS_CONNECTED)) + return; + } + + c->funcs->async_read(ac); +} + +void redisAsyncWrite(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + int done = 0; + + if (redisBufferWrite(c,&done) == REDIS_ERR) { + __redisAsyncDisconnect(ac); + } else { + /* Continue writing when not done, stop writing otherwise */ + if (!done) + _EL_ADD_WRITE(ac); + else + _EL_DEL_WRITE(ac); + + /* Always schedule reads after writes */ + _EL_ADD_READ(ac); + } +} + +void redisAsyncHandleWrite(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + + if (!(c->flags & REDIS_CONNECTED)) { + /* Abort connect was not successful. */ + if (__redisAsyncHandleConnect(ac) != REDIS_OK) + return; + /* Try again later when the context is still not connected. */ + if (!(c->flags & REDIS_CONNECTED)) + return; + } + + c->funcs->async_write(ac); +} + +void redisAsyncHandleTimeout(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + redisCallback cb; + + if ((c->flags & REDIS_CONNECTED) && ac->replies.head == NULL) { + /* Nothing to do - just an idle timeout */ + return; + } + + if (!c->err) { + __redisSetError(c, REDIS_ERR_TIMEOUT, "Timeout"); + } + + if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) { + ac->onConnect(ac, REDIS_ERR); + } + + while (__redisShiftCallback(&ac->replies, &cb) == REDIS_OK) { + __redisRunCallback(ac, &cb, NULL); + } + + /** + * TODO: Don't automatically sever the connection, + * rather, allow to ignore responses before the queue is clear + */ + __redisAsyncDisconnect(ac); +} + +/* Sets a pointer to the first argument and its length starting at p. Returns + * the number of bytes to skip to get to the following argument. */ +static const char *nextArgument(const char *start, const char **str, size_t *len) { + const char *p = start; + if (p[0] != '$') { + p = strchr(p,'$'); + if (p == NULL) return NULL; + } + + *len = (int)strtol(p+1,NULL,10); + p = strchr(p,'\r'); + assert(p); + *str = p+2; + return p+2+(*len)+2; +} + +/* Helper function for the redisAsyncCommand* family of functions. Writes a + * formatted command to the output buffer and registers the provided callback + * function with the context. */ +static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) { + redisContext *c = &(ac->c); + redisCallback cb; + struct dict *cbdict; + dictEntry *de; + redisCallback *existcb; + int pvariant, hasnext; + const char *cstr, *astr; + size_t clen, alen; + const char *p; + sds sname; + int ret; + + /* Don't accept new commands when the connection is about to be closed. */ + if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR; + + /* Setup callback */ + cb.fn = fn; + cb.privdata = privdata; + cb.pending_subs = 1; + + /* Find out which command will be appended. */ + p = nextArgument(cmd,&cstr,&clen); + assert(p != NULL); + hasnext = (p[0] == '$'); + pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0; + cstr += pvariant; + clen -= pvariant; + + if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) { + c->flags |= REDIS_SUBSCRIBED; + + /* Add every channel/pattern to the list of subscription callbacks. */ + while ((p = nextArgument(p,&astr,&alen)) != NULL) { + sname = sdsnewlen(astr,alen); + if (sname == NULL) + goto oom; + + if (pvariant) + cbdict = ac->sub.patterns; + else + cbdict = ac->sub.channels; + + de = dictFind(cbdict,sname); + + if (de != NULL) { + existcb = dictGetEntryVal(de); + cb.pending_subs = existcb->pending_subs + 1; + } + + ret = dictReplace(cbdict,sname,&cb); + + if (ret == 0) sdsfree(sname); + } + } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) { + /* It is only useful to call (P)UNSUBSCRIBE when the context is + * subscribed to one or more channels or patterns. */ + if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR; + + /* (P)UNSUBSCRIBE does not have its own response: every channel or + * pattern that is unsubscribed will receive a message. This means we + * should not append a callback function for this command. */ + } else if(strncasecmp(cstr,"monitor\r\n",9) == 0) { + /* Set monitor flag and push callback */ + c->flags |= REDIS_MONITORING; + __redisPushCallback(&ac->replies,&cb); + } else { + if (c->flags & REDIS_SUBSCRIBED) + /* This will likely result in an error reply, but it needs to be + * received and passed to the callback. */ + __redisPushCallback(&ac->sub.invalid,&cb); + else + __redisPushCallback(&ac->replies,&cb); + } + + __redisAppendCommand(c,cmd,len); + + /* Always schedule a write when the write buffer is non-empty */ + _EL_ADD_WRITE(ac); + + return REDIS_OK; +oom: + __redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; +} + +int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) { + char *cmd; + int len; + int status; + len = redisvFormatCommand(&cmd,format,ap); + + /* We don't want to pass -1 or -2 to future functions as a length. */ + if (len < 0) + return REDIS_ERR; + + status = __redisAsyncCommand(ac,fn,privdata,cmd,len); + hi_free(cmd); + return status; +} + +int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) { + va_list ap; + int status; + va_start(ap,format); + status = redisvAsyncCommand(ac,fn,privdata,format,ap); + va_end(ap); + return status; +} + +int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) { + sds cmd; + int len; + int status; + len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); + if (len < 0) + return REDIS_ERR; + status = __redisAsyncCommand(ac,fn,privdata,cmd,len); + sdsfree(cmd); + return status; +} + +int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) { + int status = __redisAsyncCommand(ac,fn,privdata,cmd,len); + return status; +} + +redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn) { + redisAsyncPushFn *old = ac->push_cb; + ac->push_cb = fn; + return old; +} + +int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) { + if (!ac->c.command_timeout) { + ac->c.command_timeout = hi_calloc(1, sizeof(tv)); + if (ac->c.command_timeout == NULL) { + __redisSetError(&ac->c, REDIS_ERR_OOM, "Out of memory"); + __redisAsyncCopyError(ac); + return REDIS_ERR; + } + } + + if (tv.tv_sec != ac->c.command_timeout->tv_sec || + tv.tv_usec != ac->c.command_timeout->tv_usec) + { + *ac->c.command_timeout = tv; + } + + return REDIS_OK; +} diff --git a/ext/hiredis-1.0.2/async.h b/ext/hiredis-1.0.2/async.h new file mode 100644 index 000000000..b1d2cb263 --- /dev/null +++ b/ext/hiredis-1.0.2/async.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_ASYNC_H +#define __HIREDIS_ASYNC_H +#include "hiredis.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ +struct dict; /* dictionary header is included in async.c */ + +/* Reply callback prototype and container */ +typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); +typedef struct redisCallback { + struct redisCallback *next; /* simple singly linked list */ + redisCallbackFn *fn; + int pending_subs; + void *privdata; +} redisCallback; + +/* List of callbacks for either regular replies or pub/sub */ +typedef struct redisCallbackList { + redisCallback *head, *tail; +} redisCallbackList; + +/* Connection callback prototypes */ +typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); +typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status); +typedef void(redisTimerCallback)(void *timer, void *privdata); + +/* Context for an async connection to Redis */ +typedef struct redisAsyncContext { + /* Hold the regular context, so it can be realloc'ed. */ + redisContext c; + + /* Setup error flags so they can be used directly. */ + int err; + char *errstr; + + /* Not used by hiredis */ + void *data; + void (*dataCleanup)(void *privdata); + + /* Event library data and hooks */ + struct { + void *data; + + /* Hooks that are called when the library expects to start + * reading/writing. These functions should be idempotent. */ + void (*addRead)(void *privdata); + void (*delRead)(void *privdata); + void (*addWrite)(void *privdata); + void (*delWrite)(void *privdata); + void (*cleanup)(void *privdata); + void (*scheduleTimer)(void *privdata, struct timeval tv); + } ev; + + /* Called when either the connection is terminated due to an error or per + * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ + redisDisconnectCallback *onDisconnect; + + /* Called when the first write event was received. */ + redisConnectCallback *onConnect; + + /* Regular command callbacks */ + redisCallbackList replies; + + /* Address used for connect() */ + struct sockaddr *saddr; + size_t addrlen; + + /* Subscription callbacks */ + struct { + redisCallbackList invalid; + struct dict *channels; + struct dict *patterns; + } sub; + + /* Any configured RESP3 PUSH handler */ + redisAsyncPushFn *push_cb; +} redisAsyncContext; + +/* Functions that proxy to hiredis */ +redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options); +redisAsyncContext *redisAsyncConnect(const char *ip, int port); +redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr); +redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, + const char *source_addr); +redisAsyncContext *redisAsyncConnectUnix(const char *path); +int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); +int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); + +redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn); +int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv); +void redisAsyncDisconnect(redisAsyncContext *ac); +void redisAsyncFree(redisAsyncContext *ac); + +/* Handle read/write events */ +void redisAsyncHandleRead(redisAsyncContext *ac); +void redisAsyncHandleWrite(redisAsyncContext *ac); +void redisAsyncHandleTimeout(redisAsyncContext *ac); +void redisAsyncRead(redisAsyncContext *ac); +void redisAsyncWrite(redisAsyncContext *ac); + +/* Command functions for an async context. Write the command to the + * output buffer and register the provided callback. */ +int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap); +int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); +int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); +int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/hiredis-1.0.2/async_private.h b/ext/hiredis-1.0.2/async_private.h new file mode 100644 index 000000000..b9d23fffd --- /dev/null +++ b/ext/hiredis-1.0.2/async_private.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_ASYNC_PRIVATE_H +#define __HIREDIS_ASYNC_PRIVATE_H + +#define _EL_ADD_READ(ctx) \ + do { \ + refreshTimeout(ctx); \ + if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \ + } while (0) +#define _EL_DEL_READ(ctx) do { \ + if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \ + } while(0) +#define _EL_ADD_WRITE(ctx) \ + do { \ + refreshTimeout(ctx); \ + if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \ + } while (0) +#define _EL_DEL_WRITE(ctx) do { \ + if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \ + } while(0) +#define _EL_CLEANUP(ctx) do { \ + if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \ + ctx->ev.cleanup = NULL; \ + } while(0); + +static inline void refreshTimeout(redisAsyncContext *ctx) { + #define REDIS_TIMER_ISSET(tvp) \ + (tvp && ((tvp)->tv_sec || (tvp)->tv_usec)) + + #define REDIS_EL_TIMER(ac, tvp) \ + if ((ac)->ev.scheduleTimer && REDIS_TIMER_ISSET(tvp)) { \ + (ac)->ev.scheduleTimer((ac)->ev.data, *(tvp)); \ + } + + if (ctx->c.flags & REDIS_CONNECTED) { + REDIS_EL_TIMER(ctx, ctx->c.command_timeout); + } else { + REDIS_EL_TIMER(ctx, ctx->c.connect_timeout); + } +} + +void __redisAsyncDisconnect(redisAsyncContext *ac); +void redisProcessCallbacks(redisAsyncContext *ac); + +#endif /* __HIREDIS_ASYNC_PRIVATE_H */ diff --git a/ext/hiredis-1.0.2/dict.c b/ext/hiredis-1.0.2/dict.c new file mode 100644 index 000000000..34a33ead9 --- /dev/null +++ b/ext/hiredis-1.0.2/dict.c @@ -0,0 +1,352 @@ +/* Hash table implementation. + * + * This file implements in memory hash tables with insert/del/replace/find/ + * get-random-element operations. Hash tables will auto resize if needed + * tables of power of two in size are used, collisions are handled by + * chaining. See the source code for more information... :) + * + * Copyright (c) 2006-2010, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include "alloc.h" +#include +#include +#include +#include "dict.h" + +/* -------------------------- private prototypes ---------------------------- */ + +static int _dictExpandIfNeeded(dict *ht); +static unsigned long _dictNextPower(unsigned long size); +static int _dictKeyIndex(dict *ht, const void *key); +static int _dictInit(dict *ht, dictType *type, void *privDataPtr); + +/* -------------------------- hash functions -------------------------------- */ + +/* Generic hash function (a popular one from Bernstein). + * I tested a few and this was the best. */ +static unsigned int dictGenHashFunction(const unsigned char *buf, int len) { + unsigned int hash = 5381; + + while (len--) + hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */ + return hash; +} + +/* ----------------------------- API implementation ------------------------- */ + +/* Reset an hashtable already initialized with ht_init(). + * NOTE: This function should only called by ht_destroy(). */ +static void _dictReset(dict *ht) { + ht->table = NULL; + ht->size = 0; + ht->sizemask = 0; + ht->used = 0; +} + +/* Create a new hash table */ +static dict *dictCreate(dictType *type, void *privDataPtr) { + dict *ht = hi_malloc(sizeof(*ht)); + if (ht == NULL) + return NULL; + + _dictInit(ht,type,privDataPtr); + return ht; +} + +/* Initialize the hash table */ +static int _dictInit(dict *ht, dictType *type, void *privDataPtr) { + _dictReset(ht); + ht->type = type; + ht->privdata = privDataPtr; + return DICT_OK; +} + +/* Expand or create the hashtable */ +static int dictExpand(dict *ht, unsigned long size) { + dict n; /* the new hashtable */ + unsigned long realsize = _dictNextPower(size), i; + + /* the size is invalid if it is smaller than the number of + * elements already inside the hashtable */ + if (ht->used > size) + return DICT_ERR; + + _dictInit(&n, ht->type, ht->privdata); + n.size = realsize; + n.sizemask = realsize-1; + n.table = hi_calloc(realsize,sizeof(dictEntry*)); + if (n.table == NULL) + return DICT_ERR; + + /* Copy all the elements from the old to the new table: + * note that if the old hash table is empty ht->size is zero, + * so dictExpand just creates an hash table. */ + n.used = ht->used; + for (i = 0; i < ht->size && ht->used > 0; i++) { + dictEntry *he, *nextHe; + + if (ht->table[i] == NULL) continue; + + /* For each hash entry on this slot... */ + he = ht->table[i]; + while(he) { + unsigned int h; + + nextHe = he->next; + /* Get the new element index */ + h = dictHashKey(ht, he->key) & n.sizemask; + he->next = n.table[h]; + n.table[h] = he; + ht->used--; + /* Pass to the next element */ + he = nextHe; + } + } + assert(ht->used == 0); + hi_free(ht->table); + + /* Remap the new hashtable in the old */ + *ht = n; + return DICT_OK; +} + +/* Add an element to the target hash table */ +static int dictAdd(dict *ht, void *key, void *val) { + int index; + dictEntry *entry; + + /* Get the index of the new element, or -1 if + * the element already exists. */ + if ((index = _dictKeyIndex(ht, key)) == -1) + return DICT_ERR; + + /* Allocates the memory and stores key */ + entry = hi_malloc(sizeof(*entry)); + if (entry == NULL) + return DICT_ERR; + + entry->next = ht->table[index]; + ht->table[index] = entry; + + /* Set the hash entry fields. */ + dictSetHashKey(ht, entry, key); + dictSetHashVal(ht, entry, val); + ht->used++; + return DICT_OK; +} + +/* Add an element, discarding the old if the key already exists. + * Return 1 if the key was added from scratch, 0 if there was already an + * element with such key and dictReplace() just performed a value update + * operation. */ +static int dictReplace(dict *ht, void *key, void *val) { + dictEntry *entry, auxentry; + + /* Try to add the element. If the key + * does not exists dictAdd will succeed. */ + if (dictAdd(ht, key, val) == DICT_OK) + return 1; + /* It already exists, get the entry */ + entry = dictFind(ht, key); + if (entry == NULL) + return 0; + + /* Free the old value and set the new one */ + /* Set the new value and free the old one. Note that it is important + * to do that in this order, as the value may just be exactly the same + * as the previous one. In this context, think to reference counting, + * you want to increment (set), and then decrement (free), and not the + * reverse. */ + auxentry = *entry; + dictSetHashVal(ht, entry, val); + dictFreeEntryVal(ht, &auxentry); + return 0; +} + +/* Search and remove an element */ +static int dictDelete(dict *ht, const void *key) { + unsigned int h; + dictEntry *de, *prevde; + + if (ht->size == 0) + return DICT_ERR; + h = dictHashKey(ht, key) & ht->sizemask; + de = ht->table[h]; + + prevde = NULL; + while(de) { + if (dictCompareHashKeys(ht,key,de->key)) { + /* Unlink the element from the list */ + if (prevde) + prevde->next = de->next; + else + ht->table[h] = de->next; + + dictFreeEntryKey(ht,de); + dictFreeEntryVal(ht,de); + hi_free(de); + ht->used--; + return DICT_OK; + } + prevde = de; + de = de->next; + } + return DICT_ERR; /* not found */ +} + +/* Destroy an entire hash table */ +static int _dictClear(dict *ht) { + unsigned long i; + + /* Free all the elements */ + for (i = 0; i < ht->size && ht->used > 0; i++) { + dictEntry *he, *nextHe; + + if ((he = ht->table[i]) == NULL) continue; + while(he) { + nextHe = he->next; + dictFreeEntryKey(ht, he); + dictFreeEntryVal(ht, he); + hi_free(he); + ht->used--; + he = nextHe; + } + } + /* Free the table and the allocated cache structure */ + hi_free(ht->table); + /* Re-initialize the table */ + _dictReset(ht); + return DICT_OK; /* never fails */ +} + +/* Clear & Release the hash table */ +static void dictRelease(dict *ht) { + _dictClear(ht); + hi_free(ht); +} + +static dictEntry *dictFind(dict *ht, const void *key) { + dictEntry *he; + unsigned int h; + + if (ht->size == 0) return NULL; + h = dictHashKey(ht, key) & ht->sizemask; + he = ht->table[h]; + while(he) { + if (dictCompareHashKeys(ht, key, he->key)) + return he; + he = he->next; + } + return NULL; +} + +static dictIterator *dictGetIterator(dict *ht) { + dictIterator *iter = hi_malloc(sizeof(*iter)); + if (iter == NULL) + return NULL; + + iter->ht = ht; + iter->index = -1; + iter->entry = NULL; + iter->nextEntry = NULL; + return iter; +} + +static dictEntry *dictNext(dictIterator *iter) { + while (1) { + if (iter->entry == NULL) { + iter->index++; + if (iter->index >= + (signed)iter->ht->size) break; + iter->entry = iter->ht->table[iter->index]; + } else { + iter->entry = iter->nextEntry; + } + if (iter->entry) { + /* We need to save the 'next' here, the iterator user + * may delete the entry we are returning. */ + iter->nextEntry = iter->entry->next; + return iter->entry; + } + } + return NULL; +} + +static void dictReleaseIterator(dictIterator *iter) { + hi_free(iter); +} + +/* ------------------------- private functions ------------------------------ */ + +/* Expand the hash table if needed */ +static int _dictExpandIfNeeded(dict *ht) { + /* If the hash table is empty expand it to the initial size, + * if the table is "full" double its size. */ + if (ht->size == 0) + return dictExpand(ht, DICT_HT_INITIAL_SIZE); + if (ht->used == ht->size) + return dictExpand(ht, ht->size*2); + return DICT_OK; +} + +/* Our hash table capability is a power of two */ +static unsigned long _dictNextPower(unsigned long size) { + unsigned long i = DICT_HT_INITIAL_SIZE; + + if (size >= LONG_MAX) return LONG_MAX; + while(1) { + if (i >= size) + return i; + i *= 2; + } +} + +/* Returns the index of a free slot that can be populated with + * an hash entry for the given 'key'. + * If the key already exists, -1 is returned. */ +static int _dictKeyIndex(dict *ht, const void *key) { + unsigned int h; + dictEntry *he; + + /* Expand the hashtable if needed */ + if (_dictExpandIfNeeded(ht) == DICT_ERR) + return -1; + /* Compute the key hash value */ + h = dictHashKey(ht, key) & ht->sizemask; + /* Search if this slot does not already contain the given key */ + he = ht->table[h]; + while(he) { + if (dictCompareHashKeys(ht, key, he->key)) + return -1; + he = he->next; + } + return h; +} + diff --git a/ext/hiredis-1.0.2/dict.h b/ext/hiredis-1.0.2/dict.h new file mode 100644 index 000000000..95fcd280e --- /dev/null +++ b/ext/hiredis-1.0.2/dict.h @@ -0,0 +1,126 @@ +/* Hash table implementation. + * + * This file implements in memory hash tables with insert/del/replace/find/ + * get-random-element operations. Hash tables will auto resize if needed + * tables of power of two in size are used, collisions are handled by + * chaining. See the source code for more information... :) + * + * Copyright (c) 2006-2010, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DICT_H +#define __DICT_H + +#define DICT_OK 0 +#define DICT_ERR 1 + +/* Unused arguments generate annoying warnings... */ +#define DICT_NOTUSED(V) ((void) V) + +typedef struct dictEntry { + void *key; + void *val; + struct dictEntry *next; +} dictEntry; + +typedef struct dictType { + unsigned int (*hashFunction)(const void *key); + void *(*keyDup)(void *privdata, const void *key); + void *(*valDup)(void *privdata, const void *obj); + int (*keyCompare)(void *privdata, const void *key1, const void *key2); + void (*keyDestructor)(void *privdata, void *key); + void (*valDestructor)(void *privdata, void *obj); +} dictType; + +typedef struct dict { + dictEntry **table; + dictType *type; + unsigned long size; + unsigned long sizemask; + unsigned long used; + void *privdata; +} dict; + +typedef struct dictIterator { + dict *ht; + int index; + dictEntry *entry, *nextEntry; +} dictIterator; + +/* This is the initial size of every hash table */ +#define DICT_HT_INITIAL_SIZE 4 + +/* ------------------------------- Macros ------------------------------------*/ +#define dictFreeEntryVal(ht, entry) \ + if ((ht)->type->valDestructor) \ + (ht)->type->valDestructor((ht)->privdata, (entry)->val) + +#define dictSetHashVal(ht, entry, _val_) do { \ + if ((ht)->type->valDup) \ + entry->val = (ht)->type->valDup((ht)->privdata, _val_); \ + else \ + entry->val = (_val_); \ +} while(0) + +#define dictFreeEntryKey(ht, entry) \ + if ((ht)->type->keyDestructor) \ + (ht)->type->keyDestructor((ht)->privdata, (entry)->key) + +#define dictSetHashKey(ht, entry, _key_) do { \ + if ((ht)->type->keyDup) \ + entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ + else \ + entry->key = (_key_); \ +} while(0) + +#define dictCompareHashKeys(ht, key1, key2) \ + (((ht)->type->keyCompare) ? \ + (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ + (key1) == (key2)) + +#define dictHashKey(ht, key) (ht)->type->hashFunction(key) + +#define dictGetEntryKey(he) ((he)->key) +#define dictGetEntryVal(he) ((he)->val) +#define dictSlots(ht) ((ht)->size) +#define dictSize(ht) ((ht)->used) + +/* API */ +static unsigned int dictGenHashFunction(const unsigned char *buf, int len); +static dict *dictCreate(dictType *type, void *privDataPtr); +static int dictExpand(dict *ht, unsigned long size); +static int dictAdd(dict *ht, void *key, void *val); +static int dictReplace(dict *ht, void *key, void *val); +static int dictDelete(dict *ht, const void *key); +static void dictRelease(dict *ht); +static dictEntry * dictFind(dict *ht, const void *key); +static dictIterator *dictGetIterator(dict *ht); +static dictEntry *dictNext(dictIterator *iter); +static void dictReleaseIterator(dictIterator *iter); + +#endif /* __DICT_H */ diff --git a/ext/hiredis-1.0.2/examples/CMakeLists.txt b/ext/hiredis-1.0.2/examples/CMakeLists.txt new file mode 100644 index 000000000..1d5bc56e0 --- /dev/null +++ b/ext/hiredis-1.0.2/examples/CMakeLists.txt @@ -0,0 +1,49 @@ +INCLUDE(FindPkgConfig) +# Check for GLib + +PKG_CHECK_MODULES(GLIB2 glib-2.0) +if (GLIB2_FOUND) + INCLUDE_DIRECTORIES(${GLIB2_INCLUDE_DIRS}) + LINK_DIRECTORIES(${GLIB2_LIBRARY_DIRS}) + ADD_EXECUTABLE(example-glib example-glib.c) + TARGET_LINK_LIBRARIES(example-glib hiredis ${GLIB2_LIBRARIES}) +ENDIF(GLIB2_FOUND) + +FIND_PATH(LIBEV ev.h + HINTS /usr/local /usr/opt/local + ENV LIBEV_INCLUDE_DIR) + +if (LIBEV) + # Just compile and link with libev + ADD_EXECUTABLE(example-libev example-libev.c) + TARGET_LINK_LIBRARIES(example-libev hiredis ev) +ENDIF() + +FIND_PATH(LIBEVENT event.h) +if (LIBEVENT) + ADD_EXECUTABLE(example-libevent example-libevent) + TARGET_LINK_LIBRARIES(example-libevent hiredis event) +ENDIF() + +FIND_PATH(LIBUV uv.h) +IF (LIBUV) + ADD_EXECUTABLE(example-libuv example-libuv.c) + TARGET_LINK_LIBRARIES(example-libuv hiredis uv) +ENDIF() + +IF (APPLE) + FIND_LIBRARY(CF CoreFoundation) + ADD_EXECUTABLE(example-macosx example-macosx.c) + TARGET_LINK_LIBRARIES(example-macosx hiredis ${CF}) +ENDIF() + +IF (ENABLE_SSL) + ADD_EXECUTABLE(example-ssl example-ssl.c) + TARGET_LINK_LIBRARIES(example-ssl hiredis hiredis_ssl) +ENDIF() + +ADD_EXECUTABLE(example example.c) +TARGET_LINK_LIBRARIES(example hiredis) + +ADD_EXECUTABLE(example-push example-push.c) +TARGET_LINK_LIBRARIES(example-push hiredis) diff --git a/ext/hiredis-1.0.2/examples/example-ae.c b/ext/hiredis-1.0.2/examples/example-ae.c new file mode 100644 index 000000000..8efa7306a --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-ae.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include + +#include +#include +#include + +/* Put event loop in the global scope, so it can be explicitly stopped */ +static aeEventLoop *loop; + +void getCallback(redisAsyncContext *c, void *r, void *privdata) { + redisReply *reply = r; + if (reply == NULL) return; + printf("argv[%s]: %s\n", (char*)privdata, reply->str); + + /* Disconnect after receiving the reply to GET */ + redisAsyncDisconnect(c); +} + +void connectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + aeStop(loop); + return; + } + + printf("Connected...\n"); +} + +void disconnectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + aeStop(loop); + return; + } + + printf("Disconnected...\n"); + aeStop(loop); +} + +int main (int argc, char **argv) { + signal(SIGPIPE, SIG_IGN); + + redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); + if (c->err) { + /* Let *c leak for now... */ + printf("Error: %s\n", c->errstr); + return 1; + } + + loop = aeCreateEventLoop(64); + redisAeAttach(loop, c); + redisAsyncSetConnectCallback(c,connectCallback); + redisAsyncSetDisconnectCallback(c,disconnectCallback); + redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); + redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); + aeMain(loop); + return 0; +} + diff --git a/ext/hiredis-1.0.2/examples/example-glib.c b/ext/hiredis-1.0.2/examples/example-glib.c new file mode 100644 index 000000000..d6e10f8e8 --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-glib.c @@ -0,0 +1,73 @@ +#include + +#include +#include +#include + +static GMainLoop *mainloop; + +static void +connect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, + int status) +{ + if (status != REDIS_OK) { + g_printerr("Failed to connect: %s\n", ac->errstr); + g_main_loop_quit(mainloop); + } else { + g_printerr("Connected...\n"); + } +} + +static void +disconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, + int status) +{ + if (status != REDIS_OK) { + g_error("Failed to disconnect: %s", ac->errstr); + } else { + g_printerr("Disconnected...\n"); + g_main_loop_quit(mainloop); + } +} + +static void +command_cb(redisAsyncContext *ac, + gpointer r, + gpointer user_data G_GNUC_UNUSED) +{ + redisReply *reply = r; + + if (reply) { + g_print("REPLY: %s\n", reply->str); + } + + redisAsyncDisconnect(ac); +} + +gint +main (gint argc G_GNUC_UNUSED, + gchar *argv[] G_GNUC_UNUSED) +{ + redisAsyncContext *ac; + GMainContext *context = NULL; + GSource *source; + + ac = redisAsyncConnect("127.0.0.1", 6379); + if (ac->err) { + g_printerr("%s\n", ac->errstr); + exit(EXIT_FAILURE); + } + + source = redis_source_new(ac); + mainloop = g_main_loop_new(context, FALSE); + g_source_attach(source, context); + + redisAsyncSetConnectCallback(ac, connect_cb); + redisAsyncSetDisconnectCallback(ac, disconnect_cb); + redisAsyncCommand(ac, command_cb, NULL, "SET key 1234"); + redisAsyncCommand(ac, command_cb, NULL, "GET key"); + + g_main_loop_run(mainloop); + + return EXIT_SUCCESS; +} diff --git a/ext/hiredis-1.0.2/examples/example-ivykis.c b/ext/hiredis-1.0.2/examples/example-ivykis.c new file mode 100644 index 000000000..f57dc3887 --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-ivykis.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#include +#include +#include + +void getCallback(redisAsyncContext *c, void *r, void *privdata) { + redisReply *reply = r; + if (reply == NULL) return; + printf("argv[%s]: %s\n", (char*)privdata, reply->str); + + /* Disconnect after receiving the reply to GET */ + redisAsyncDisconnect(c); +} + +void connectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + printf("Connected...\n"); +} + +void disconnectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + printf("Disconnected...\n"); +} + +int main (int argc, char **argv) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + + iv_init(); + + redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); + if (c->err) { + /* Let *c leak for now... */ + printf("Error: %s\n", c->errstr); + return 1; + } + + redisIvykisAttach(c); + redisAsyncSetConnectCallback(c,connectCallback); + redisAsyncSetDisconnectCallback(c,disconnectCallback); + redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); + redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); + + iv_main(); + + iv_deinit(); + + return 0; +} diff --git a/ext/hiredis-1.0.2/examples/example-libev.c b/ext/hiredis-1.0.2/examples/example-libev.c new file mode 100644 index 000000000..ec474306b --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-libev.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#include +#include +#include + +void getCallback(redisAsyncContext *c, void *r, void *privdata) { + redisReply *reply = r; + if (reply == NULL) return; + printf("argv[%s]: %s\n", (char*)privdata, reply->str); + + /* Disconnect after receiving the reply to GET */ + redisAsyncDisconnect(c); +} + +void connectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + printf("Connected...\n"); +} + +void disconnectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + printf("Disconnected...\n"); +} + +int main (int argc, char **argv) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + + redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); + if (c->err) { + /* Let *c leak for now... */ + printf("Error: %s\n", c->errstr); + return 1; + } + + redisLibevAttach(EV_DEFAULT_ c); + redisAsyncSetConnectCallback(c,connectCallback); + redisAsyncSetDisconnectCallback(c,disconnectCallback); + redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); + redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); + ev_loop(EV_DEFAULT_ 0); + return 0; +} diff --git a/ext/hiredis-1.0.2/examples/example-libevent-ssl.c b/ext/hiredis-1.0.2/examples/example-libevent-ssl.c new file mode 100644 index 000000000..7d99af1ba --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-libevent-ssl.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +void getCallback(redisAsyncContext *c, void *r, void *privdata) { + redisReply *reply = r; + if (reply == NULL) return; + printf("argv[%s]: %s\n", (char*)privdata, reply->str); + + /* Disconnect after receiving the reply to GET */ + redisAsyncDisconnect(c); +} + +void connectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + printf("Connected...\n"); +} + +void disconnectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + printf("Disconnected...\n"); +} + +int main (int argc, char **argv) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + + struct event_base *base = event_base_new(); + if (argc < 5) { + fprintf(stderr, + "Usage: %s [ca]\n", argv[0]); + exit(1); + } + + const char *value = argv[1]; + size_t nvalue = strlen(value); + + const char *hostname = argv[2]; + int port = atoi(argv[3]); + + const char *cert = argv[4]; + const char *certKey = argv[5]; + const char *caCert = argc > 5 ? argv[6] : NULL; + + redisSSLContext *ssl; + redisSSLContextError ssl_error; + + redisInitOpenSSL(); + + ssl = redisCreateSSLContext(caCert, NULL, + cert, certKey, NULL, &ssl_error); + if (!ssl) { + printf("Error: %s\n", redisSSLContextGetError(ssl_error)); + return 1; + } + + redisAsyncContext *c = redisAsyncConnect(hostname, port); + if (c->err) { + /* Let *c leak for now... */ + printf("Error: %s\n", c->errstr); + return 1; + } + if (redisInitiateSSLWithContext(&c->c, ssl) != REDIS_OK) { + printf("SSL Error!\n"); + exit(1); + } + + redisLibeventAttach(c,base); + redisAsyncSetConnectCallback(c,connectCallback); + redisAsyncSetDisconnectCallback(c,disconnectCallback); + redisAsyncCommand(c, NULL, NULL, "SET key %b", value, nvalue); + redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); + event_base_dispatch(base); + + redisFreeSSLContext(ssl); + return 0; +} diff --git a/ext/hiredis-1.0.2/examples/example-libevent.c b/ext/hiredis-1.0.2/examples/example-libevent.c new file mode 100644 index 000000000..49bddd0c2 --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-libevent.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +#include +#include +#include + +void getCallback(redisAsyncContext *c, void *r, void *privdata) { + redisReply *reply = r; + if (reply == NULL) { + if (c->errstr) { + printf("errstr: %s\n", c->errstr); + } + return; + } + printf("argv[%s]: %s\n", (char*)privdata, reply->str); + + /* Disconnect after receiving the reply to GET */ + redisAsyncDisconnect(c); +} + +void connectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + printf("Connected...\n"); +} + +void disconnectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + printf("Disconnected...\n"); +} + +int main (int argc, char **argv) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + + struct event_base *base = event_base_new(); + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379); + struct timeval tv = {0}; + tv.tv_sec = 1; + options.connect_timeout = &tv; + + + redisAsyncContext *c = redisAsyncConnectWithOptions(&options); + if (c->err) { + /* Let *c leak for now... */ + printf("Error: %s\n", c->errstr); + return 1; + } + + redisLibeventAttach(c,base); + redisAsyncSetConnectCallback(c,connectCallback); + redisAsyncSetDisconnectCallback(c,disconnectCallback); + redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); + redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); + event_base_dispatch(base); + return 0; +} diff --git a/ext/hiredis-1.0.2/examples/example-libuv.c b/ext/hiredis-1.0.2/examples/example-libuv.c new file mode 100644 index 000000000..cbde452b9 --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-libuv.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include + +#include +#include +#include + +void getCallback(redisAsyncContext *c, void *r, void *privdata) { + redisReply *reply = r; + if (reply == NULL) return; + printf("argv[%s]: %s\n", (char*)privdata, reply->str); + + /* Disconnect after receiving the reply to GET */ + redisAsyncDisconnect(c); +} + +void connectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + printf("Connected...\n"); +} + +void disconnectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + printf("Disconnected...\n"); +} + +int main (int argc, char **argv) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + + uv_loop_t* loop = uv_default_loop(); + + redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); + if (c->err) { + /* Let *c leak for now... */ + printf("Error: %s\n", c->errstr); + return 1; + } + + redisLibuvAttach(c,loop); + redisAsyncSetConnectCallback(c,connectCallback); + redisAsyncSetDisconnectCallback(c,disconnectCallback); + redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); + redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); + uv_run(loop, UV_RUN_DEFAULT); + return 0; +} diff --git a/ext/hiredis-1.0.2/examples/example-macosx.c b/ext/hiredis-1.0.2/examples/example-macosx.c new file mode 100644 index 000000000..bc84ed5ba --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-macosx.c @@ -0,0 +1,66 @@ +// +// Created by Дмитрий Бахвалов on 13.07.15. +// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved. +// + +#include + +#include +#include +#include + +void getCallback(redisAsyncContext *c, void *r, void *privdata) { + redisReply *reply = r; + if (reply == NULL) return; + printf("argv[%s]: %s\n", (char*)privdata, reply->str); + + /* Disconnect after receiving the reply to GET */ + redisAsyncDisconnect(c); +} + +void connectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + printf("Connected...\n"); +} + +void disconnectCallback(const redisAsyncContext *c, int status) { + if (status != REDIS_OK) { + printf("Error: %s\n", c->errstr); + return; + } + CFRunLoopStop(CFRunLoopGetCurrent()); + printf("Disconnected...\n"); +} + +int main (int argc, char **argv) { + signal(SIGPIPE, SIG_IGN); + + CFRunLoopRef loop = CFRunLoopGetCurrent(); + if( !loop ) { + printf("Error: Cannot get current run loop\n"); + return 1; + } + + redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); + if (c->err) { + /* Let *c leak for now... */ + printf("Error: %s\n", c->errstr); + return 1; + } + + redisMacOSAttach(c, loop); + + redisAsyncSetConnectCallback(c,connectCallback); + redisAsyncSetDisconnectCallback(c,disconnectCallback); + + redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); + redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); + + CFRunLoopRun(); + + return 0; +} + diff --git a/ext/hiredis-1.0.2/examples/example-push.c b/ext/hiredis-1.0.2/examples/example-push.c new file mode 100644 index 000000000..2d4ab4dc0 --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-push.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2020, Michael Grunder + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#define KEY_COUNT 5 + +#define panicAbort(fmt, ...) \ + do { \ + fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, __VA_ARGS__); \ + exit(-1); \ + } while (0) + +static void assertReplyAndFree(redisContext *context, redisReply *reply, int type) { + if (reply == NULL) + panicAbort("NULL reply from server (error: %s)", context->errstr); + + if (reply->type != type) { + if (reply->type == REDIS_REPLY_ERROR) + fprintf(stderr, "Redis Error: %s\n", reply->str); + + panicAbort("Expected reply type %d but got type %d", type, reply->type); + } + + freeReplyObject(reply); +} + +/* Switch to the RESP3 protocol and enable client tracking */ +static void enableClientTracking(redisContext *c) { + redisReply *reply = redisCommand(c, "HELLO 3"); + if (reply == NULL || c->err) { + panicAbort("NULL reply or server error (error: %s)", c->errstr); + } + + if (reply->type != REDIS_REPLY_MAP) { + fprintf(stderr, "Error: Can't send HELLO 3 command. Are you sure you're "); + fprintf(stderr, "connected to redis-server >= 6.0.0?\nRedis error: %s\n", + reply->type == REDIS_REPLY_ERROR ? reply->str : "(unknown)"); + exit(-1); + } + + freeReplyObject(reply); + + /* Enable client tracking */ + reply = redisCommand(c, "CLIENT TRACKING ON"); + assertReplyAndFree(c, reply, REDIS_REPLY_STATUS); +} + +void pushReplyHandler(void *privdata, void *r) { + redisReply *reply = r; + int *invalidations = privdata; + + /* Sanity check on the invalidation reply */ + if (reply->type != REDIS_REPLY_PUSH || reply->elements != 2 || + reply->element[1]->type != REDIS_REPLY_ARRAY || + reply->element[1]->element[0]->type != REDIS_REPLY_STRING) + { + panicAbort("%s", "Can't parse PUSH message!"); + } + + /* Increment our invalidation count */ + *invalidations += 1; + + printf("pushReplyHandler(): INVALIDATE '%s' (invalidation count: %d)\n", + reply->element[1]->element[0]->str, *invalidations); + + freeReplyObject(reply); +} + +/* We aren't actually freeing anything here, but it is included to show that we can + * have hiredis call our data destructor when freeing the context */ +void privdata_dtor(void *privdata) { + unsigned int *icount = privdata; + printf("privdata_dtor(): In context privdata dtor (invalidations: %u)\n", *icount); +} + +int main(int argc, char **argv) { + unsigned int j, invalidations = 0; + redisContext *c; + redisReply *reply; + + const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1"; + int port = (argc > 2) ? atoi(argv[2]) : 6379; + + redisOptions o = {0}; + REDIS_OPTIONS_SET_TCP(&o, hostname, port); + + /* Set our context privdata to the address of our invalidation counter. Each + * time our PUSH handler is called, hiredis will pass the privdata for context. + * + * This could also be done after we create the context like so: + * + * c->privdata = &invalidations; + * c->free_privdata = privdata_dtor; + */ + REDIS_OPTIONS_SET_PRIVDATA(&o, &invalidations, privdata_dtor); + + /* Set our custom PUSH message handler */ + o.push_cb = pushReplyHandler; + + c = redisConnectWithOptions(&o); + if (c == NULL || c->err) + panicAbort("Connection error: %s", c ? c->errstr : "OOM"); + + /* Enable RESP3 and turn on client tracking */ + enableClientTracking(c); + + /* Set some keys and then read them back. Once we do that, Redis will deliver + * invalidation push messages whenever the key is modified */ + for (j = 0; j < KEY_COUNT; j++) { + reply = redisCommand(c, "SET key:%d initial:%d", j, j); + assertReplyAndFree(c, reply, REDIS_REPLY_STATUS); + + reply = redisCommand(c, "GET key:%d", j); + assertReplyAndFree(c, reply, REDIS_REPLY_STRING); + } + + /* Trigger invalidation messages by updating keys we just read */ + for (j = 0; j < KEY_COUNT; j++) { + printf(" main(): SET key:%d update:%d\n", j, j); + reply = redisCommand(c, "SET key:%d update:%d", j, j); + assertReplyAndFree(c, reply, REDIS_REPLY_STATUS); + printf(" main(): SET REPLY OK\n"); + } + + printf("\nTotal detected invalidations: %d, expected: %d\n", invalidations, KEY_COUNT); + + /* PING server */ + redisFree(c); +} diff --git a/ext/hiredis-1.0.2/examples/example-qt.cpp b/ext/hiredis-1.0.2/examples/example-qt.cpp new file mode 100644 index 000000000..f524c3f3d --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-qt.cpp @@ -0,0 +1,46 @@ +#include +using namespace std; + +#include +#include + +#include "example-qt.h" + +void getCallback(redisAsyncContext *, void * r, void * privdata) { + + redisReply * reply = static_cast(r); + ExampleQt * ex = static_cast(privdata); + if (reply == nullptr || ex == nullptr) return; + + cout << "key: " << reply->str << endl; + + ex->finish(); +} + +void ExampleQt::run() { + + m_ctx = redisAsyncConnect("localhost", 6379); + + if (m_ctx->err) { + cerr << "Error: " << m_ctx->errstr << endl; + redisAsyncFree(m_ctx); + emit finished(); + } + + m_adapter.setContext(m_ctx); + + redisAsyncCommand(m_ctx, NULL, NULL, "SET key %s", m_value); + redisAsyncCommand(m_ctx, getCallback, this, "GET key"); +} + +int main (int argc, char **argv) { + + QCoreApplication app(argc, argv); + + ExampleQt example(argv[argc-1]); + + QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit())); + QTimer::singleShot(0, &example, SLOT(run())); + + return app.exec(); +} diff --git a/ext/hiredis-1.0.2/examples/example-qt.h b/ext/hiredis-1.0.2/examples/example-qt.h new file mode 100644 index 000000000..374f47666 --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-qt.h @@ -0,0 +1,32 @@ +#ifndef __HIREDIS_EXAMPLE_QT_H +#define __HIREDIS_EXAMPLE_QT_H + +#include + +class ExampleQt : public QObject { + + Q_OBJECT + + public: + ExampleQt(const char * value, QObject * parent = 0) + : QObject(parent), m_value(value) {} + + signals: + void finished(); + + public slots: + void run(); + + private: + void finish() { emit finished(); } + + private: + const char * m_value; + redisAsyncContext * m_ctx; + RedisQtAdapter m_adapter; + + friend + void getCallback(redisAsyncContext *, void *, void *); +}; + +#endif /* !__HIREDIS_EXAMPLE_QT_H */ diff --git a/ext/hiredis-1.0.2/examples/example-ssl.c b/ext/hiredis-1.0.2/examples/example-ssl.c new file mode 100644 index 000000000..c754177cf --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example-ssl.c @@ -0,0 +1,110 @@ +#include +#include +#include + +#include +#include +#include + +int main(int argc, char **argv) { + unsigned int j; + redisSSLContext *ssl; + redisSSLContextError ssl_error; + redisContext *c; + redisReply *reply; + if (argc < 4) { + printf("Usage: %s [ca]\n", argv[0]); + exit(1); + } + const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1"; + int port = atoi(argv[2]); + const char *cert = argv[3]; + const char *key = argv[4]; + const char *ca = argc > 4 ? argv[5] : NULL; + + redisInitOpenSSL(); + ssl = redisCreateSSLContext(ca, NULL, cert, key, NULL, &ssl_error); + if (!ssl) { + printf("SSL Context error: %s\n", + redisSSLContextGetError(ssl_error)); + exit(1); + } + + struct timeval tv = { 1, 500000 }; // 1.5 seconds + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, hostname, port); + options.connect_timeout = &tv; + c = redisConnectWithOptions(&options); + + if (c == NULL || c->err) { + if (c) { + printf("Connection error: %s\n", c->errstr); + redisFree(c); + } else { + printf("Connection error: can't allocate redis context\n"); + } + exit(1); + } + + if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) { + printf("Couldn't initialize SSL!\n"); + printf("Error: %s\n", c->errstr); + redisFree(c); + exit(1); + } + + /* PING server */ + reply = redisCommand(c,"PING"); + printf("PING: %s\n", reply->str); + freeReplyObject(reply); + + /* Set a key */ + reply = redisCommand(c,"SET %s %s", "foo", "hello world"); + printf("SET: %s\n", reply->str); + freeReplyObject(reply); + + /* Set a key using binary safe API */ + reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5); + printf("SET (binary API): %s\n", reply->str); + freeReplyObject(reply); + + /* Try a GET and two INCR */ + reply = redisCommand(c,"GET foo"); + printf("GET foo: %s\n", reply->str); + freeReplyObject(reply); + + reply = redisCommand(c,"INCR counter"); + printf("INCR counter: %lld\n", reply->integer); + freeReplyObject(reply); + /* again ... */ + reply = redisCommand(c,"INCR counter"); + printf("INCR counter: %lld\n", reply->integer); + freeReplyObject(reply); + + /* Create a list of numbers, from 0 to 9 */ + reply = redisCommand(c,"DEL mylist"); + freeReplyObject(reply); + for (j = 0; j < 10; j++) { + char buf[64]; + + snprintf(buf,64,"%u",j); + reply = redisCommand(c,"LPUSH mylist element-%s", buf); + freeReplyObject(reply); + } + + /* Let's check what we have inside the list */ + reply = redisCommand(c,"LRANGE mylist 0 -1"); + if (reply->type == REDIS_REPLY_ARRAY) { + for (j = 0; j < reply->elements; j++) { + printf("%u) %s\n", j, reply->element[j]->str); + } + } + freeReplyObject(reply); + + /* Disconnects and frees the context */ + redisFree(c); + + redisFreeSSLContext(ssl); + + return 0; +} diff --git a/ext/hiredis-1.0.2/examples/example.c b/ext/hiredis-1.0.2/examples/example.c new file mode 100644 index 000000000..15dacbd18 --- /dev/null +++ b/ext/hiredis-1.0.2/examples/example.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + unsigned int j, isunix = 0; + redisContext *c; + redisReply *reply; + const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1"; + + if (argc > 2) { + if (*argv[2] == 'u' || *argv[2] == 'U') { + isunix = 1; + /* in this case, host is the path to the unix socket */ + printf("Will connect to unix socket @%s\n", hostname); + } + } + + int port = (argc > 2) ? atoi(argv[2]) : 6379; + + struct timeval timeout = { 1, 500000 }; // 1.5 seconds + if (isunix) { + c = redisConnectUnixWithTimeout(hostname, timeout); + } else { + c = redisConnectWithTimeout(hostname, port, timeout); + } + if (c == NULL || c->err) { + if (c) { + printf("Connection error: %s\n", c->errstr); + redisFree(c); + } else { + printf("Connection error: can't allocate redis context\n"); + } + exit(1); + } + + /* PING server */ + reply = redisCommand(c,"PING"); + printf("PING: %s\n", reply->str); + freeReplyObject(reply); + + /* Set a key */ + reply = redisCommand(c,"SET %s %s", "foo", "hello world"); + printf("SET: %s\n", reply->str); + freeReplyObject(reply); + + /* Set a key using binary safe API */ + reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5); + printf("SET (binary API): %s\n", reply->str); + freeReplyObject(reply); + + /* Try a GET and two INCR */ + reply = redisCommand(c,"GET foo"); + printf("GET foo: %s\n", reply->str); + freeReplyObject(reply); + + reply = redisCommand(c,"INCR counter"); + printf("INCR counter: %lld\n", reply->integer); + freeReplyObject(reply); + /* again ... */ + reply = redisCommand(c,"INCR counter"); + printf("INCR counter: %lld\n", reply->integer); + freeReplyObject(reply); + + /* Create a list of numbers, from 0 to 9 */ + reply = redisCommand(c,"DEL mylist"); + freeReplyObject(reply); + for (j = 0; j < 10; j++) { + char buf[64]; + + snprintf(buf,64,"%u",j); + reply = redisCommand(c,"LPUSH mylist element-%s", buf); + freeReplyObject(reply); + } + + /* Let's check what we have inside the list */ + reply = redisCommand(c,"LRANGE mylist 0 -1"); + if (reply->type == REDIS_REPLY_ARRAY) { + for (j = 0; j < reply->elements; j++) { + printf("%u) %s\n", j, reply->element[j]->str); + } + } + freeReplyObject(reply); + + /* Disconnects and frees the context */ + redisFree(c); + + return 0; +} diff --git a/ext/hiredis-1.0.2/fmacros.h b/ext/hiredis-1.0.2/fmacros.h new file mode 100644 index 000000000..3227faafd --- /dev/null +++ b/ext/hiredis-1.0.2/fmacros.h @@ -0,0 +1,12 @@ +#ifndef __HIREDIS_FMACRO_H +#define __HIREDIS_FMACRO_H + +#define _XOPEN_SOURCE 600 +#define _POSIX_C_SOURCE 200112L + +#if defined(__APPLE__) && defined(__MACH__) +/* Enable TCP_KEEPALIVE */ +#define _DARWIN_C_SOURCE +#endif + +#endif diff --git a/ext/hiredis-1.0.2/hiredis-config.cmake.in b/ext/hiredis-1.0.2/hiredis-config.cmake.in new file mode 100644 index 000000000..98851dcee --- /dev/null +++ b/ext/hiredis-1.0.2/hiredis-config.cmake.in @@ -0,0 +1,13 @@ +@PACKAGE_INIT@ + +set_and_check(hiredis_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@") + +IF (NOT TARGET hiredis::hiredis) + INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis-targets.cmake) +ENDIF() + +SET(hiredis_LIBRARIES hiredis::hiredis) +SET(hiredis_INCLUDE_DIRS ${hiredis_INCLUDEDIR}) + +check_required_components(hiredis) + diff --git a/ext/hiredis-1.0.2/hiredis.c b/ext/hiredis-1.0.2/hiredis.c new file mode 100644 index 000000000..ab0e39822 --- /dev/null +++ b/ext/hiredis-1.0.2/hiredis.c @@ -0,0 +1,1174 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2014, Pieter Noordhuis + * Copyright (c) 2015, Matt Stancliff , + * Jan-Erik Rediger + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include +#include +#include +#include +#include + +#include "hiredis.h" +#include "net.h" +#include "sds.h" +#include "async.h" +#include "win32.h" + +extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout); +extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout); + +static redisContextFuncs redisContextDefaultFuncs = { + .free_privctx = NULL, + .async_read = redisAsyncRead, + .async_write = redisAsyncWrite, + .read = redisNetRead, + .write = redisNetWrite +}; + +static redisReply *createReplyObject(int type); +static void *createStringObject(const redisReadTask *task, char *str, size_t len); +static void *createArrayObject(const redisReadTask *task, size_t elements); +static void *createIntegerObject(const redisReadTask *task, long long value); +static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len); +static void *createNilObject(const redisReadTask *task); +static void *createBoolObject(const redisReadTask *task, int bval); + +/* Default set of functions to build the reply. Keep in mind that such a + * function returning NULL is interpreted as OOM. */ +static redisReplyObjectFunctions defaultFunctions = { + createStringObject, + createArrayObject, + createIntegerObject, + createDoubleObject, + createNilObject, + createBoolObject, + freeReplyObject +}; + +/* Create a reply object */ +static redisReply *createReplyObject(int type) { + redisReply *r = hi_calloc(1,sizeof(*r)); + + if (r == NULL) + return NULL; + + r->type = type; + return r; +} + +/* Free a reply object */ +void freeReplyObject(void *reply) { + redisReply *r = reply; + size_t j; + + if (r == NULL) + return; + + switch(r->type) { + case REDIS_REPLY_INTEGER: + break; /* Nothing to free */ + case REDIS_REPLY_ARRAY: + case REDIS_REPLY_MAP: + case REDIS_REPLY_SET: + case REDIS_REPLY_PUSH: + if (r->element != NULL) { + for (j = 0; j < r->elements; j++) + freeReplyObject(r->element[j]); + hi_free(r->element); + } + break; + case REDIS_REPLY_ERROR: + case REDIS_REPLY_STATUS: + case REDIS_REPLY_STRING: + case REDIS_REPLY_DOUBLE: + case REDIS_REPLY_VERB: + hi_free(r->str); + break; + } + hi_free(r); +} + +static void *createStringObject(const redisReadTask *task, char *str, size_t len) { + redisReply *r, *parent; + char *buf; + + r = createReplyObject(task->type); + if (r == NULL) + return NULL; + + assert(task->type == REDIS_REPLY_ERROR || + task->type == REDIS_REPLY_STATUS || + task->type == REDIS_REPLY_STRING || + task->type == REDIS_REPLY_VERB); + + /* Copy string value */ + if (task->type == REDIS_REPLY_VERB) { + buf = hi_malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */ + if (buf == NULL) goto oom; + + memcpy(r->vtype,str,3); + r->vtype[3] = '\0'; + memcpy(buf,str+4,len-4); + buf[len-4] = '\0'; + r->len = len - 4; + } else { + buf = hi_malloc(len+1); + if (buf == NULL) goto oom; + + memcpy(buf,str,len); + buf[len] = '\0'; + r->len = len; + } + r->str = buf; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); + parent->element[task->idx] = r; + } + return r; + +oom: + freeReplyObject(r); + return NULL; +} + +static void *createArrayObject(const redisReadTask *task, size_t elements) { + redisReply *r, *parent; + + r = createReplyObject(task->type); + if (r == NULL) + return NULL; + + if (elements > 0) { + if (SIZE_MAX / sizeof(redisReply*) < elements) return NULL; /* Don't overflow */ + r->element = hi_calloc(elements,sizeof(redisReply*)); + if (r->element == NULL) { + freeReplyObject(r); + return NULL; + } + } + + r->elements = elements; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); + parent->element[task->idx] = r; + } + return r; +} + +static void *createIntegerObject(const redisReadTask *task, long long value) { + redisReply *r, *parent; + + r = createReplyObject(REDIS_REPLY_INTEGER); + if (r == NULL) + return NULL; + + r->integer = value; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); + parent->element[task->idx] = r; + } + return r; +} + +static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) { + redisReply *r, *parent; + + r = createReplyObject(REDIS_REPLY_DOUBLE); + if (r == NULL) + return NULL; + + r->dval = value; + r->str = hi_malloc(len+1); + if (r->str == NULL) { + freeReplyObject(r); + return NULL; + } + + /* The double reply also has the original protocol string representing a + * double as a null terminated string. This way the caller does not need + * to format back for string conversion, especially since Redis does efforts + * to make the string more human readable avoiding the calssical double + * decimal string conversion artifacts. */ + memcpy(r->str, str, len); + r->str[len] = '\0'; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET); + parent->element[task->idx] = r; + } + return r; +} + +static void *createNilObject(const redisReadTask *task) { + redisReply *r, *parent; + + r = createReplyObject(REDIS_REPLY_NIL); + if (r == NULL) + return NULL; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET); + parent->element[task->idx] = r; + } + return r; +} + +static void *createBoolObject(const redisReadTask *task, int bval) { + redisReply *r, *parent; + + r = createReplyObject(REDIS_REPLY_BOOL); + if (r == NULL) + return NULL; + + r->integer = bval != 0; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET); + parent->element[task->idx] = r; + } + return r; +} + +/* Return the number of digits of 'v' when converted to string in radix 10. + * Implementation borrowed from link in redis/src/util.c:string2ll(). */ +static uint32_t countDigits(uint64_t v) { + uint32_t result = 1; + for (;;) { + if (v < 10) return result; + if (v < 100) return result + 1; + if (v < 1000) return result + 2; + if (v < 10000) return result + 3; + v /= 10000U; + result += 4; + } +} + +/* Helper that calculates the bulk length given a certain string length. */ +static size_t bulklen(size_t len) { + return 1+countDigits(len)+2+len+2; +} + +int redisvFormatCommand(char **target, const char *format, va_list ap) { + const char *c = format; + char *cmd = NULL; /* final command */ + int pos; /* position in final command */ + sds curarg, newarg; /* current argument */ + int touched = 0; /* was the current argument touched? */ + char **curargv = NULL, **newargv = NULL; + int argc = 0; + int totlen = 0; + int error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */ + int j; + + /* Abort if there is not target to set */ + if (target == NULL) + return -1; + + /* Build the command string accordingly to protocol */ + curarg = sdsempty(); + if (curarg == NULL) + return -1; + + while(*c != '\0') { + if (*c != '%' || c[1] == '\0') { + if (*c == ' ') { + if (touched) { + newargv = hi_realloc(curargv,sizeof(char*)*(argc+1)); + if (newargv == NULL) goto memory_err; + curargv = newargv; + curargv[argc++] = curarg; + totlen += bulklen(sdslen(curarg)); + + /* curarg is put in argv so it can be overwritten. */ + curarg = sdsempty(); + if (curarg == NULL) goto memory_err; + touched = 0; + } + } else { + newarg = sdscatlen(curarg,c,1); + if (newarg == NULL) goto memory_err; + curarg = newarg; + touched = 1; + } + } else { + char *arg; + size_t size; + + /* Set newarg so it can be checked even if it is not touched. */ + newarg = curarg; + + switch(c[1]) { + case 's': + arg = va_arg(ap,char*); + size = strlen(arg); + if (size > 0) + newarg = sdscatlen(curarg,arg,size); + break; + case 'b': + arg = va_arg(ap,char*); + size = va_arg(ap,size_t); + if (size > 0) + newarg = sdscatlen(curarg,arg,size); + break; + case '%': + newarg = sdscat(curarg,"%"); + break; + default: + /* Try to detect printf format */ + { + static const char intfmts[] = "diouxX"; + static const char flags[] = "#0-+ "; + char _format[16]; + const char *_p = c+1; + size_t _l = 0; + va_list _cpy; + + /* Flags */ + while (*_p != '\0' && strchr(flags,*_p) != NULL) _p++; + + /* Field width */ + while (*_p != '\0' && isdigit(*_p)) _p++; + + /* Precision */ + if (*_p == '.') { + _p++; + while (*_p != '\0' && isdigit(*_p)) _p++; + } + + /* Copy va_list before consuming with va_arg */ + va_copy(_cpy,ap); + + /* Integer conversion (without modifiers) */ + if (strchr(intfmts,*_p) != NULL) { + va_arg(ap,int); + goto fmt_valid; + } + + /* Double conversion (without modifiers) */ + if (strchr("eEfFgGaA",*_p) != NULL) { + va_arg(ap,double); + goto fmt_valid; + } + + /* Size: char */ + if (_p[0] == 'h' && _p[1] == 'h') { + _p += 2; + if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { + va_arg(ap,int); /* char gets promoted to int */ + goto fmt_valid; + } + goto fmt_invalid; + } + + /* Size: short */ + if (_p[0] == 'h') { + _p += 1; + if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { + va_arg(ap,int); /* short gets promoted to int */ + goto fmt_valid; + } + goto fmt_invalid; + } + + /* Size: long long */ + if (_p[0] == 'l' && _p[1] == 'l') { + _p += 2; + if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { + va_arg(ap,long long); + goto fmt_valid; + } + goto fmt_invalid; + } + + /* Size: long */ + if (_p[0] == 'l') { + _p += 1; + if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { + va_arg(ap,long); + goto fmt_valid; + } + goto fmt_invalid; + } + + fmt_invalid: + va_end(_cpy); + goto format_err; + + fmt_valid: + _l = (_p+1)-c; + if (_l < sizeof(_format)-2) { + memcpy(_format,c,_l); + _format[_l] = '\0'; + newarg = sdscatvprintf(curarg,_format,_cpy); + + /* Update current position (note: outer blocks + * increment c twice so compensate here) */ + c = _p-1; + } + + va_end(_cpy); + break; + } + } + + if (newarg == NULL) goto memory_err; + curarg = newarg; + + touched = 1; + c++; + } + c++; + } + + /* Add the last argument if needed */ + if (touched) { + newargv = hi_realloc(curargv,sizeof(char*)*(argc+1)); + if (newargv == NULL) goto memory_err; + curargv = newargv; + curargv[argc++] = curarg; + totlen += bulklen(sdslen(curarg)); + } else { + sdsfree(curarg); + } + + /* Clear curarg because it was put in curargv or was free'd. */ + curarg = NULL; + + /* Add bytes needed to hold multi bulk count */ + totlen += 1+countDigits(argc)+2; + + /* Build the command at protocol level */ + cmd = hi_malloc(totlen+1); + if (cmd == NULL) goto memory_err; + + pos = sprintf(cmd,"*%d\r\n",argc); + for (j = 0; j < argc; j++) { + pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j])); + memcpy(cmd+pos,curargv[j],sdslen(curargv[j])); + pos += sdslen(curargv[j]); + sdsfree(curargv[j]); + cmd[pos++] = '\r'; + cmd[pos++] = '\n'; + } + assert(pos == totlen); + cmd[pos] = '\0'; + + hi_free(curargv); + *target = cmd; + return totlen; + +format_err: + error_type = -2; + goto cleanup; + +memory_err: + error_type = -1; + goto cleanup; + +cleanup: + if (curargv) { + while(argc--) + sdsfree(curargv[argc]); + hi_free(curargv); + } + + sdsfree(curarg); + hi_free(cmd); + + return error_type; +} + +/* Format a command according to the Redis protocol. This function + * takes a format similar to printf: + * + * %s represents a C null terminated string you want to interpolate + * %b represents a binary safe string + * + * When using %b you need to provide both the pointer to the string + * and the length in bytes as a size_t. Examples: + * + * len = redisFormatCommand(target, "GET %s", mykey); + * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen); + */ +int redisFormatCommand(char **target, const char *format, ...) { + va_list ap; + int len; + va_start(ap,format); + len = redisvFormatCommand(target,format,ap); + va_end(ap); + + /* The API says "-1" means bad result, but we now also return "-2" in some + * cases. Force the return value to always be -1. */ + if (len < 0) + len = -1; + + return len; +} + +/* Format a command according to the Redis protocol using an sds string and + * sdscatfmt for the processing of arguments. This function takes the + * number of arguments, an array with arguments and an array with their + * lengths. If the latter is set to NULL, strlen will be used to compute the + * argument lengths. + */ +int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv, + const size_t *argvlen) +{ + sds cmd, aux; + unsigned long long totlen; + int j; + size_t len; + + /* Abort on a NULL target */ + if (target == NULL) + return -1; + + /* Calculate our total size */ + totlen = 1+countDigits(argc)+2; + for (j = 0; j < argc; j++) { + len = argvlen ? argvlen[j] : strlen(argv[j]); + totlen += bulklen(len); + } + + /* Use an SDS string for command construction */ + cmd = sdsempty(); + if (cmd == NULL) + return -1; + + /* We already know how much storage we need */ + aux = sdsMakeRoomFor(cmd, totlen); + if (aux == NULL) { + sdsfree(cmd); + return -1; + } + + cmd = aux; + + /* Construct command */ + cmd = sdscatfmt(cmd, "*%i\r\n", argc); + for (j=0; j < argc; j++) { + len = argvlen ? argvlen[j] : strlen(argv[j]); + cmd = sdscatfmt(cmd, "$%u\r\n", len); + cmd = sdscatlen(cmd, argv[j], len); + cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1); + } + + assert(sdslen(cmd)==totlen); + + *target = cmd; + return totlen; +} + +void redisFreeSdsCommand(sds cmd) { + sdsfree(cmd); +} + +/* Format a command according to the Redis protocol. This function takes the + * number of arguments, an array with arguments and an array with their + * lengths. If the latter is set to NULL, strlen will be used to compute the + * argument lengths. + */ +int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) { + char *cmd = NULL; /* final command */ + int pos; /* position in final command */ + size_t len; + int totlen, j; + + /* Abort on a NULL target */ + if (target == NULL) + return -1; + + /* Calculate number of bytes needed for the command */ + totlen = 1+countDigits(argc)+2; + for (j = 0; j < argc; j++) { + len = argvlen ? argvlen[j] : strlen(argv[j]); + totlen += bulklen(len); + } + + /* Build the command at protocol level */ + cmd = hi_malloc(totlen+1); + if (cmd == NULL) + return -1; + + pos = sprintf(cmd,"*%d\r\n",argc); + for (j = 0; j < argc; j++) { + len = argvlen ? argvlen[j] : strlen(argv[j]); + pos += sprintf(cmd+pos,"$%zu\r\n",len); + memcpy(cmd+pos,argv[j],len); + pos += len; + cmd[pos++] = '\r'; + cmd[pos++] = '\n'; + } + assert(pos == totlen); + cmd[pos] = '\0'; + + *target = cmd; + return totlen; +} + +void redisFreeCommand(char *cmd) { + hi_free(cmd); +} + +void __redisSetError(redisContext *c, int type, const char *str) { + size_t len; + + c->err = type; + if (str != NULL) { + len = strlen(str); + len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1); + memcpy(c->errstr,str,len); + c->errstr[len] = '\0'; + } else { + /* Only REDIS_ERR_IO may lack a description! */ + assert(type == REDIS_ERR_IO); + strerror_r(errno, c->errstr, sizeof(c->errstr)); + } +} + +redisReader *redisReaderCreate(void) { + return redisReaderCreateWithFunctions(&defaultFunctions); +} + +static void redisPushAutoFree(void *privdata, void *reply) { + (void)privdata; + freeReplyObject(reply); +} + +static redisContext *redisContextInit(void) { + redisContext *c; + + c = hi_calloc(1, sizeof(*c)); + if (c == NULL) + return NULL; + + c->funcs = &redisContextDefaultFuncs; + + c->obuf = sdsempty(); + c->reader = redisReaderCreate(); + c->fd = REDIS_INVALID_FD; + + if (c->obuf == NULL || c->reader == NULL) { + redisFree(c); + return NULL; + } + + return c; +} + +void redisFree(redisContext *c) { + if (c == NULL) + return; + redisNetClose(c); + + sdsfree(c->obuf); + redisReaderFree(c->reader); + hi_free(c->tcp.host); + hi_free(c->tcp.source_addr); + hi_free(c->unix_sock.path); + hi_free(c->connect_timeout); + hi_free(c->command_timeout); + hi_free(c->saddr); + + if (c->privdata && c->free_privdata) + c->free_privdata(c->privdata); + + if (c->funcs->free_privctx) + c->funcs->free_privctx(c->privctx); + + memset(c, 0xff, sizeof(*c)); + hi_free(c); +} + +redisFD redisFreeKeepFd(redisContext *c) { + redisFD fd = c->fd; + c->fd = REDIS_INVALID_FD; + redisFree(c); + return fd; +} + +int redisReconnect(redisContext *c) { + c->err = 0; + memset(c->errstr, '\0', strlen(c->errstr)); + + if (c->privctx && c->funcs->free_privctx) { + c->funcs->free_privctx(c->privctx); + c->privctx = NULL; + } + + redisNetClose(c); + + sdsfree(c->obuf); + redisReaderFree(c->reader); + + c->obuf = sdsempty(); + c->reader = redisReaderCreate(); + + if (c->obuf == NULL || c->reader == NULL) { + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; + } + + int ret = REDIS_ERR; + if (c->connection_type == REDIS_CONN_TCP) { + ret = redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port, + c->connect_timeout, c->tcp.source_addr); + } else if (c->connection_type == REDIS_CONN_UNIX) { + ret = redisContextConnectUnix(c, c->unix_sock.path, c->connect_timeout); + } else { + /* Something bad happened here and shouldn't have. There isn't + enough information in the context to reconnect. */ + __redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect"); + ret = REDIS_ERR; + } + + if (c->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) { + redisContextSetTimeout(c, *c->command_timeout); + } + + return ret; +} + +redisContext *redisConnectWithOptions(const redisOptions *options) { + redisContext *c = redisContextInit(); + if (c == NULL) { + return NULL; + } + if (!(options->options & REDIS_OPT_NONBLOCK)) { + c->flags |= REDIS_BLOCK; + } + if (options->options & REDIS_OPT_REUSEADDR) { + c->flags |= REDIS_REUSEADDR; + } + if (options->options & REDIS_OPT_NOAUTOFREE) { + c->flags |= REDIS_NO_AUTO_FREE; + } + + /* Set any user supplied RESP3 PUSH handler or use freeReplyObject + * as a default unless specifically flagged that we don't want one. */ + if (options->push_cb != NULL) + redisSetPushCallback(c, options->push_cb); + else if (!(options->options & REDIS_OPT_NO_PUSH_AUTOFREE)) + redisSetPushCallback(c, redisPushAutoFree); + + c->privdata = options->privdata; + c->free_privdata = options->free_privdata; + + if (redisContextUpdateConnectTimeout(c, options->connect_timeout) != REDIS_OK || + redisContextUpdateCommandTimeout(c, options->command_timeout) != REDIS_OK) { + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return c; + } + + if (options->type == REDIS_CONN_TCP) { + redisContextConnectBindTcp(c, options->endpoint.tcp.ip, + options->endpoint.tcp.port, options->connect_timeout, + options->endpoint.tcp.source_addr); + } else if (options->type == REDIS_CONN_UNIX) { + redisContextConnectUnix(c, options->endpoint.unix_socket, + options->connect_timeout); + } else if (options->type == REDIS_CONN_USERFD) { + c->fd = options->endpoint.fd; + c->flags |= REDIS_CONNECTED; + } else { + // Unknown type - FIXME - FREE + return NULL; + } + + if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) { + redisContextSetTimeout(c, *options->command_timeout); + } + + return c; +} + +/* Connect to a Redis instance. On error the field error in the returned + * context will be set to the return value of the error function. + * When no set of reply functions is given, the default set will be used. */ +redisContext *redisConnect(const char *ip, int port) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + return redisConnectWithOptions(&options); +} + +redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.connect_timeout = &tv; + return redisConnectWithOptions(&options); +} + +redisContext *redisConnectNonBlock(const char *ip, int port) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.options |= REDIS_OPT_NONBLOCK; + return redisConnectWithOptions(&options); +} + +redisContext *redisConnectBindNonBlock(const char *ip, int port, + const char *source_addr) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.endpoint.tcp.source_addr = source_addr; + options.options |= REDIS_OPT_NONBLOCK; + return redisConnectWithOptions(&options); +} + +redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, + const char *source_addr) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.endpoint.tcp.source_addr = source_addr; + options.options |= REDIS_OPT_NONBLOCK|REDIS_OPT_REUSEADDR; + return redisConnectWithOptions(&options); +} + +redisContext *redisConnectUnix(const char *path) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_UNIX(&options, path); + return redisConnectWithOptions(&options); +} + +redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_UNIX(&options, path); + options.connect_timeout = &tv; + return redisConnectWithOptions(&options); +} + +redisContext *redisConnectUnixNonBlock(const char *path) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_UNIX(&options, path); + options.options |= REDIS_OPT_NONBLOCK; + return redisConnectWithOptions(&options); +} + +redisContext *redisConnectFd(redisFD fd) { + redisOptions options = {0}; + options.type = REDIS_CONN_USERFD; + options.endpoint.fd = fd; + return redisConnectWithOptions(&options); +} + +/* Set read/write timeout on a blocking socket. */ +int redisSetTimeout(redisContext *c, const struct timeval tv) { + if (c->flags & REDIS_BLOCK) + return redisContextSetTimeout(c,tv); + return REDIS_ERR; +} + +/* Enable connection KeepAlive. */ +int redisEnableKeepAlive(redisContext *c) { + if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK) + return REDIS_ERR; + return REDIS_OK; +} + +/* Set a user provided RESP3 PUSH handler and return any old one set. */ +redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn) { + redisPushFn *old = c->push_cb; + c->push_cb = fn; + return old; +} + +/* Use this function to handle a read event on the descriptor. It will try + * and read some bytes from the socket and feed them to the reply parser. + * + * After this function is called, you may use redisGetReplyFromReader to + * see if there is a reply available. */ +int redisBufferRead(redisContext *c) { + char buf[1024*16]; + int nread; + + /* Return early when the context has seen an error. */ + if (c->err) + return REDIS_ERR; + + nread = c->funcs->read(c, buf, sizeof(buf)); + if (nread > 0) { + if (redisReaderFeed(c->reader, buf, nread) != REDIS_OK) { + __redisSetError(c, c->reader->err, c->reader->errstr); + return REDIS_ERR; + } else { + } + } else if (nread < 0) { + return REDIS_ERR; + } + return REDIS_OK; +} + +/* Write the output buffer to the socket. + * + * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was + * successfully written to the socket. When the buffer is empty after the + * write operation, "done" is set to 1 (if given). + * + * Returns REDIS_ERR if an error occurred trying to write and sets + * c->errstr to hold the appropriate error string. + */ +int redisBufferWrite(redisContext *c, int *done) { + + /* Return early when the context has seen an error. */ + if (c->err) + return REDIS_ERR; + + if (sdslen(c->obuf) > 0) { + ssize_t nwritten = c->funcs->write(c); + if (nwritten < 0) { + return REDIS_ERR; + } else if (nwritten > 0) { + if (nwritten == (ssize_t)sdslen(c->obuf)) { + sdsfree(c->obuf); + c->obuf = sdsempty(); + if (c->obuf == NULL) + goto oom; + } else { + if (sdsrange(c->obuf,nwritten,-1) < 0) goto oom; + } + } + } + if (done != NULL) *done = (sdslen(c->obuf) == 0); + return REDIS_OK; + +oom: + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; +} + +/* Internal helper function to try and get a reply from the reader, + * or set an error in the context otherwise. */ +int redisGetReplyFromReader(redisContext *c, void **reply) { + if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { + __redisSetError(c,c->reader->err,c->reader->errstr); + return REDIS_ERR; + } + + return REDIS_OK; +} + +/* Internal helper that returns 1 if the reply was a RESP3 PUSH + * message and we handled it with a user-provided callback. */ +static int redisHandledPushReply(redisContext *c, void *reply) { + if (reply && c->push_cb && redisIsPushReply(reply)) { + c->push_cb(c->privdata, reply); + return 1; + } + + return 0; +} + +int redisGetReply(redisContext *c, void **reply) { + int wdone = 0; + void *aux = NULL; + + /* Try to read pending replies */ + if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) + return REDIS_ERR; + + /* For the blocking context, flush output buffer and read reply */ + if (aux == NULL && c->flags & REDIS_BLOCK) { + /* Write until done */ + do { + if (redisBufferWrite(c,&wdone) == REDIS_ERR) + return REDIS_ERR; + } while (!wdone); + + /* Read until there is a reply */ + do { + if (redisBufferRead(c) == REDIS_ERR) + return REDIS_ERR; + + /* We loop here in case the user has specified a RESP3 + * PUSH handler (e.g. for client tracking). */ + do { + if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) + return REDIS_ERR; + } while (redisHandledPushReply(c, aux)); + } while (aux == NULL); + } + + /* Set reply or free it if we were passed NULL */ + if (reply != NULL) { + *reply = aux; + } else { + freeReplyObject(aux); + } + + return REDIS_OK; +} + + +/* Helper function for the redisAppendCommand* family of functions. + * + * Write a formatted command to the output buffer. When this family + * is used, you need to call redisGetReply yourself to retrieve + * the reply (or replies in pub/sub). + */ +int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) { + sds newbuf; + + newbuf = sdscatlen(c->obuf,cmd,len); + if (newbuf == NULL) { + __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); + return REDIS_ERR; + } + + c->obuf = newbuf; + return REDIS_OK; +} + +int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) { + + if (__redisAppendCommand(c, cmd, len) != REDIS_OK) { + return REDIS_ERR; + } + + return REDIS_OK; +} + +int redisvAppendCommand(redisContext *c, const char *format, va_list ap) { + char *cmd; + int len; + + len = redisvFormatCommand(&cmd,format,ap); + if (len == -1) { + __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); + return REDIS_ERR; + } else if (len == -2) { + __redisSetError(c,REDIS_ERR_OTHER,"Invalid format string"); + return REDIS_ERR; + } + + if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { + hi_free(cmd); + return REDIS_ERR; + } + + hi_free(cmd); + return REDIS_OK; +} + +int redisAppendCommand(redisContext *c, const char *format, ...) { + va_list ap; + int ret; + + va_start(ap,format); + ret = redisvAppendCommand(c,format,ap); + va_end(ap); + return ret; +} + +int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { + sds cmd; + int len; + + len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); + if (len == -1) { + __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); + return REDIS_ERR; + } + + if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { + sdsfree(cmd); + return REDIS_ERR; + } + + sdsfree(cmd); + return REDIS_OK; +} + +/* Helper function for the redisCommand* family of functions. + * + * Write a formatted command to the output buffer. If the given context is + * blocking, immediately read the reply into the "reply" pointer. When the + * context is non-blocking, the "reply" pointer will not be used and the + * command is simply appended to the write buffer. + * + * Returns the reply when a reply was successfully retrieved. Returns NULL + * otherwise. When NULL is returned in a blocking context, the error field + * in the context will be set. + */ +static void *__redisBlockForReply(redisContext *c) { + void *reply; + + if (c->flags & REDIS_BLOCK) { + if (redisGetReply(c,&reply) != REDIS_OK) + return NULL; + return reply; + } + return NULL; +} + +void *redisvCommand(redisContext *c, const char *format, va_list ap) { + if (redisvAppendCommand(c,format,ap) != REDIS_OK) + return NULL; + return __redisBlockForReply(c); +} + +void *redisCommand(redisContext *c, const char *format, ...) { + va_list ap; + va_start(ap,format); + void *reply = redisvCommand(c,format,ap); + va_end(ap); + return reply; +} + +void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { + if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK) + return NULL; + return __redisBlockForReply(c); +} diff --git a/ext/hiredis-1.0.2/hiredis.h b/ext/hiredis-1.0.2/hiredis.h new file mode 100644 index 000000000..3bc46d992 --- /dev/null +++ b/ext/hiredis-1.0.2/hiredis.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2014, Pieter Noordhuis + * Copyright (c) 2015, Matt Stancliff , + * Jan-Erik Rediger + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_H +#define __HIREDIS_H +#include "read.h" +#include /* for va_list */ +#ifndef _MSC_VER +#include /* for struct timeval */ +#else +struct timeval; /* forward declaration */ +typedef long long ssize_t; +#endif +#include /* uintXX_t, etc */ +#include "sds.h" /* for sds */ +#include "alloc.h" /* for allocation wrappers */ + +#define HIREDIS_MAJOR 1 +#define HIREDIS_MINOR 0 +#define HIREDIS_PATCH 2 +#define HIREDIS_SONAME 1.0.0 + +/* Connection type can be blocking or non-blocking and is set in the + * least significant bit of the flags field in redisContext. */ +#define REDIS_BLOCK 0x1 + +/* Connection may be disconnected before being free'd. The second bit + * in the flags field is set when the context is connected. */ +#define REDIS_CONNECTED 0x2 + +/* The async API might try to disconnect cleanly and flush the output + * buffer and read all subsequent replies before disconnecting. + * This flag means no new commands can come in and the connection + * should be terminated once all replies have been read. */ +#define REDIS_DISCONNECTING 0x4 + +/* Flag specific to the async API which means that the context should be clean + * up as soon as possible. */ +#define REDIS_FREEING 0x8 + +/* Flag that is set when an async callback is executed. */ +#define REDIS_IN_CALLBACK 0x10 + +/* Flag that is set when the async context has one or more subscriptions. */ +#define REDIS_SUBSCRIBED 0x20 + +/* Flag that is set when monitor mode is active */ +#define REDIS_MONITORING 0x40 + +/* Flag that is set when we should set SO_REUSEADDR before calling bind() */ +#define REDIS_REUSEADDR 0x80 + +/** + * Flag that indicates the user does not want the context to + * be automatically freed upon error + */ +#define REDIS_NO_AUTO_FREE 0x200 + +#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ + +/* number of times we retry to connect in the case of EADDRNOTAVAIL and + * SO_REUSEADDR is being used. */ +#define REDIS_CONNECT_RETRIES 10 + +/* Forward declarations for structs defined elsewhere */ +struct redisAsyncContext; +struct redisContext; + +/* RESP3 push helpers and callback prototypes */ +#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH) +typedef void (redisPushFn)(void *, void *); +typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *); + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the reply object returned by redisCommand() */ +typedef struct redisReply { + int type; /* REDIS_REPLY_* */ + long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ + double dval; /* The double when type is REDIS_REPLY_DOUBLE */ + size_t len; /* Length of string */ + char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING + REDIS_REPLY_VERB, and REDIS_REPLY_DOUBLE (in additional to dval). */ + char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null + terminated 3 character content type, such as "txt". */ + size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ + struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ +} redisReply; + +redisReader *redisReaderCreate(void); + +/* Function to free the reply objects hiredis returns by default. */ +void freeReplyObject(void *reply); + +/* Functions to format a command according to the protocol. */ +int redisvFormatCommand(char **target, const char *format, va_list ap); +int redisFormatCommand(char **target, const char *format, ...); +int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen); +int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen); +void redisFreeCommand(char *cmd); +void redisFreeSdsCommand(sds cmd); + +enum redisConnectionType { + REDIS_CONN_TCP, + REDIS_CONN_UNIX, + REDIS_CONN_USERFD +}; + +struct redisSsl; + +#define REDIS_OPT_NONBLOCK 0x01 +#define REDIS_OPT_REUSEADDR 0x02 + +/** + * Don't automatically free the async object on a connection failure, + * or other implicit conditions. Only free on an explicit call to disconnect() or free() + */ +#define REDIS_OPT_NOAUTOFREE 0x04 + +/* Don't automatically intercept and free RESP3 PUSH replies. */ +#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08 + +/* In Unix systems a file descriptor is a regular signed int, with -1 + * representing an invalid descriptor. In Windows it is a SOCKET + * (32- or 64-bit unsigned integer depending on the architecture), where + * all bits set (~0) is INVALID_SOCKET. */ +#ifndef _WIN32 +typedef int redisFD; +#define REDIS_INVALID_FD -1 +#else +#ifdef _WIN64 +typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */ +#else +typedef unsigned long redisFD; /* SOCKET = 32-bit UINT_PTR */ +#endif +#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */ +#endif + +typedef struct { + /* + * the type of connection to use. This also indicates which + * `endpoint` member field to use + */ + int type; + /* bit field of REDIS_OPT_xxx */ + int options; + /* timeout value for connect operation. If NULL, no timeout is used */ + const struct timeval *connect_timeout; + /* timeout value for commands. If NULL, no timeout is used. This can be + * updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */ + const struct timeval *command_timeout; + union { + /** use this field for tcp/ip connections */ + struct { + const char *source_addr; + const char *ip; + int port; + } tcp; + /** use this field for unix domain sockets */ + const char *unix_socket; + /** + * use this field to have hiredis operate an already-open + * file descriptor */ + redisFD fd; + } endpoint; + + /* Optional user defined data/destructor */ + void *privdata; + void (*free_privdata)(void *); + + /* A user defined PUSH message callback */ + redisPushFn *push_cb; + redisAsyncPushFn *async_push_cb; +} redisOptions; + +/** + * Helper macros to initialize options to their specified fields. + */ +#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \ + (opts)->type = REDIS_CONN_TCP; \ + (opts)->endpoint.tcp.ip = ip_; \ + (opts)->endpoint.tcp.port = port_; + +#define REDIS_OPTIONS_SET_UNIX(opts, path) \ + (opts)->type = REDIS_CONN_UNIX; \ + (opts)->endpoint.unix_socket = path; + +#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \ + (opts)->privdata = data; \ + (opts)->free_privdata = dtor; \ + +typedef struct redisContextFuncs { + void (*free_privctx)(void *); + void (*async_read)(struct redisAsyncContext *); + void (*async_write)(struct redisAsyncContext *); + ssize_t (*read)(struct redisContext *, char *, size_t); + ssize_t (*write)(struct redisContext *); +} redisContextFuncs; + +/* Context for a connection to Redis */ +typedef struct redisContext { + const redisContextFuncs *funcs; /* Function table */ + + int err; /* Error flags, 0 when there is no error */ + char errstr[128]; /* String representation of error when applicable */ + redisFD fd; + int flags; + char *obuf; /* Write buffer */ + redisReader *reader; /* Protocol reader */ + + enum redisConnectionType connection_type; + struct timeval *connect_timeout; + struct timeval *command_timeout; + + struct { + char *host; + char *source_addr; + int port; + } tcp; + + struct { + char *path; + } unix_sock; + + /* For non-blocking connect */ + struct sockadr *saddr; + size_t addrlen; + + /* Optional data and corresponding destructor users can use to provide + * context to a given redisContext. Not used by hiredis. */ + void *privdata; + void (*free_privdata)(void *); + + /* Internal context pointer presently used by hiredis to manage + * SSL connections. */ + void *privctx; + + /* An optional RESP3 PUSH handler */ + redisPushFn *push_cb; +} redisContext; + +redisContext *redisConnectWithOptions(const redisOptions *options); +redisContext *redisConnect(const char *ip, int port); +redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); +redisContext *redisConnectNonBlock(const char *ip, int port); +redisContext *redisConnectBindNonBlock(const char *ip, int port, + const char *source_addr); +redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, + const char *source_addr); +redisContext *redisConnectUnix(const char *path); +redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); +redisContext *redisConnectUnixNonBlock(const char *path); +redisContext *redisConnectFd(redisFD fd); + +/** + * Reconnect the given context using the saved information. + * + * This re-uses the exact same connect options as in the initial connection. + * host, ip (or path), timeout and bind address are reused, + * flags are used unmodified from the existing context. + * + * Returns REDIS_OK on successful connect or REDIS_ERR otherwise. + */ +int redisReconnect(redisContext *c); + +redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn); +int redisSetTimeout(redisContext *c, const struct timeval tv); +int redisEnableKeepAlive(redisContext *c); +void redisFree(redisContext *c); +redisFD redisFreeKeepFd(redisContext *c); +int redisBufferRead(redisContext *c); +int redisBufferWrite(redisContext *c, int *done); + +/* In a blocking context, this function first checks if there are unconsumed + * replies to return and returns one if so. Otherwise, it flushes the output + * buffer to the socket and reads until it has a reply. In a non-blocking + * context, it will return unconsumed replies until there are no more. */ +int redisGetReply(redisContext *c, void **reply); +int redisGetReplyFromReader(redisContext *c, void **reply); + +/* Write a formatted command to the output buffer. Use these functions in blocking mode + * to get a pipeline of commands. */ +int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len); + +/* Write a command to the output buffer. Use these functions in blocking mode + * to get a pipeline of commands. */ +int redisvAppendCommand(redisContext *c, const char *format, va_list ap); +int redisAppendCommand(redisContext *c, const char *format, ...); +int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); + +/* Issue a command to Redis. In a blocking context, it is identical to calling + * redisAppendCommand, followed by redisGetReply. The function will return + * NULL if there was an error in performing the request, otherwise it will + * return the reply. In a non-blocking context, it is identical to calling + * only redisAppendCommand and will always return NULL. */ +void *redisvCommand(redisContext *c, const char *format, va_list ap); +void *redisCommand(redisContext *c, const char *format, ...); +void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/hiredis-1.0.2/hiredis.pc.in b/ext/hiredis-1.0.2/hiredis.pc.in new file mode 100644 index 000000000..91b773183 --- /dev/null +++ b/ext/hiredis-1.0.2/hiredis.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +install_libdir=@CMAKE_INSTALL_LIBDIR@ +exec_prefix=${prefix} +libdir=${exec_prefix}/${install_libdir} +includedir=${prefix}/include +pkgincludedir=${includedir}/hiredis + +Name: hiredis +Description: Minimalistic C client library for Redis. +Version: @PROJECT_VERSION@ +Libs: -L${libdir} -lhiredis +Cflags: -I${pkgincludedir} -D_FILE_OFFSET_BITS=64 diff --git a/ext/hiredis-1.0.2/hiredis_ssl-config.cmake.in b/ext/hiredis-1.0.2/hiredis_ssl-config.cmake.in new file mode 100644 index 000000000..9a283dfc2 --- /dev/null +++ b/ext/hiredis-1.0.2/hiredis_ssl-config.cmake.in @@ -0,0 +1,13 @@ +@PACKAGE_INIT@ + +set_and_check(hiredis_ssl_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@") + +IF (NOT TARGET hiredis::hiredis_ssl) + INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis_ssl-targets.cmake) +ENDIF() + +SET(hiredis_ssl_LIBRARIES hiredis::hiredis_ssl) +SET(hiredis_ssl_INCLUDE_DIRS ${hiredis_ssl_INCLUDEDIR}) + +check_required_components(hiredis_ssl) + diff --git a/ext/hiredis-1.0.2/hiredis_ssl.h b/ext/hiredis-1.0.2/hiredis_ssl.h new file mode 100644 index 000000000..604efe0c1 --- /dev/null +++ b/ext/hiredis-1.0.2/hiredis_ssl.h @@ -0,0 +1,127 @@ + +/* + * Copyright (c) 2019, Redis Labs + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_SSL_H +#define __HIREDIS_SSL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the underlying struct for SSL in ssl.h, which is not included to + * keep build dependencies short here. + */ +struct ssl_st; + +/* A wrapper around OpenSSL SSL_CTX to allow easy SSL use without directly + * calling OpenSSL. + */ +typedef struct redisSSLContext redisSSLContext; + +/** + * Initialization errors that redisCreateSSLContext() may return. + */ + +typedef enum { + REDIS_SSL_CTX_NONE = 0, /* No Error */ + REDIS_SSL_CTX_CREATE_FAILED, /* Failed to create OpenSSL SSL_CTX */ + REDIS_SSL_CTX_CERT_KEY_REQUIRED, /* Client cert and key must both be specified or skipped */ + REDIS_SSL_CTX_CA_CERT_LOAD_FAILED, /* Failed to load CA Certificate or CA Path */ + REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED, /* Failed to load client certificate */ + REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED /* Failed to load private key */ +} redisSSLContextError; + +/** + * Return the error message corresponding with the specified error code. + */ + +const char *redisSSLContextGetError(redisSSLContextError error); + +/** + * Helper function to initialize the OpenSSL library. + * + * OpenSSL requires one-time initialization before it can be used. Callers should + * call this function only once, and only if OpenSSL is not directly initialized + * elsewhere. + */ +int redisInitOpenSSL(void); + +/** + * Helper function to initialize an OpenSSL context that can be used + * to initiate SSL connections. + * + * cacert_filename is an optional name of a CA certificate/bundle file to load + * and use for validation. + * + * capath is an optional directory path where trusted CA certificate files are + * stored in an OpenSSL-compatible structure. + * + * cert_filename and private_key_filename are optional names of a client side + * certificate and private key files to use for authentication. They need to + * be both specified or omitted. + * + * server_name is an optional and will be used as a server name indication + * (SNI) TLS extension. + * + * If error is non-null, it will be populated in case the context creation fails + * (returning a NULL). + */ + +redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath, + const char *cert_filename, const char *private_key_filename, + const char *server_name, redisSSLContextError *error); + +/** + * Free a previously created OpenSSL context. + */ +void redisFreeSSLContext(redisSSLContext *redis_ssl_ctx); + +/** + * Initiate SSL on an existing redisContext. + * + * This is similar to redisInitiateSSL() but does not require the caller + * to directly interact with OpenSSL, and instead uses a redisSSLContext + * previously created using redisCreateSSLContext(). + */ + +int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx); + +/** + * Initiate SSL/TLS negotiation on a provided OpenSSL SSL object. + */ + +int redisInitiateSSL(redisContext *c, struct ssl_st *ssl); + +#ifdef __cplusplus +} +#endif + +#endif /* __HIREDIS_SSL_H */ diff --git a/ext/hiredis-1.0.2/hiredis_ssl.pc.in b/ext/hiredis-1.0.2/hiredis_ssl.pc.in new file mode 100644 index 000000000..588a978a5 --- /dev/null +++ b/ext/hiredis-1.0.2/hiredis_ssl.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include +pkgincludedir=${includedir}/hiredis + +Name: hiredis_ssl +Description: SSL Support for hiredis. +Version: @PROJECT_VERSION@ +Requires: hiredis +Libs: -L${libdir} -lhiredis_ssl +Libs.private: -lssl -lcrypto diff --git a/ext/hiredis-1.0.2/include/hiredis/alloc.h b/ext/hiredis-1.0.2/include/hiredis/alloc.h new file mode 100644 index 000000000..34a05f49f --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/alloc.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020, Michael Grunder + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HIREDIS_ALLOC_H +#define HIREDIS_ALLOC_H + +#include /* for size_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure pointing to our actually configured allocators */ +typedef struct hiredisAllocFuncs { + void *(*mallocFn)(size_t); + void *(*callocFn)(size_t,size_t); + void *(*reallocFn)(void*,size_t); + char *(*strdupFn)(const char*); + void (*freeFn)(void*); +} hiredisAllocFuncs; + +hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha); +void hiredisResetAllocators(void); + +#ifndef _WIN32 + +/* Hiredis' configured allocator function pointer struct */ +extern hiredisAllocFuncs hiredisAllocFns; + +static inline void *hi_malloc(size_t size) { + return hiredisAllocFns.mallocFn(size); +} + +static inline void *hi_calloc(size_t nmemb, size_t size) { + return hiredisAllocFns.callocFn(nmemb, size); +} + +static inline void *hi_realloc(void *ptr, size_t size) { + return hiredisAllocFns.reallocFn(ptr, size); +} + +static inline char *hi_strdup(const char *str) { + return hiredisAllocFns.strdupFn(str); +} + +static inline void hi_free(void *ptr) { + hiredisAllocFns.freeFn(ptr); +} + +#else + +void *hi_malloc(size_t size); +void *hi_calloc(size_t nmemb, size_t size); +void *hi_realloc(void *ptr, size_t size); +char *hi_strdup(const char *str); +void hi_free(void *ptr); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HIREDIS_ALLOC_H */ diff --git a/ext/hiredis-1.0.2/include/hiredis/async.h b/ext/hiredis-1.0.2/include/hiredis/async.h new file mode 100644 index 000000000..b1d2cb263 --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/async.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_ASYNC_H +#define __HIREDIS_ASYNC_H +#include "hiredis.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ +struct dict; /* dictionary header is included in async.c */ + +/* Reply callback prototype and container */ +typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); +typedef struct redisCallback { + struct redisCallback *next; /* simple singly linked list */ + redisCallbackFn *fn; + int pending_subs; + void *privdata; +} redisCallback; + +/* List of callbacks for either regular replies or pub/sub */ +typedef struct redisCallbackList { + redisCallback *head, *tail; +} redisCallbackList; + +/* Connection callback prototypes */ +typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); +typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status); +typedef void(redisTimerCallback)(void *timer, void *privdata); + +/* Context for an async connection to Redis */ +typedef struct redisAsyncContext { + /* Hold the regular context, so it can be realloc'ed. */ + redisContext c; + + /* Setup error flags so they can be used directly. */ + int err; + char *errstr; + + /* Not used by hiredis */ + void *data; + void (*dataCleanup)(void *privdata); + + /* Event library data and hooks */ + struct { + void *data; + + /* Hooks that are called when the library expects to start + * reading/writing. These functions should be idempotent. */ + void (*addRead)(void *privdata); + void (*delRead)(void *privdata); + void (*addWrite)(void *privdata); + void (*delWrite)(void *privdata); + void (*cleanup)(void *privdata); + void (*scheduleTimer)(void *privdata, struct timeval tv); + } ev; + + /* Called when either the connection is terminated due to an error or per + * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ + redisDisconnectCallback *onDisconnect; + + /* Called when the first write event was received. */ + redisConnectCallback *onConnect; + + /* Regular command callbacks */ + redisCallbackList replies; + + /* Address used for connect() */ + struct sockaddr *saddr; + size_t addrlen; + + /* Subscription callbacks */ + struct { + redisCallbackList invalid; + struct dict *channels; + struct dict *patterns; + } sub; + + /* Any configured RESP3 PUSH handler */ + redisAsyncPushFn *push_cb; +} redisAsyncContext; + +/* Functions that proxy to hiredis */ +redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options); +redisAsyncContext *redisAsyncConnect(const char *ip, int port); +redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr); +redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, + const char *source_addr); +redisAsyncContext *redisAsyncConnectUnix(const char *path); +int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); +int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); + +redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn); +int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv); +void redisAsyncDisconnect(redisAsyncContext *ac); +void redisAsyncFree(redisAsyncContext *ac); + +/* Handle read/write events */ +void redisAsyncHandleRead(redisAsyncContext *ac); +void redisAsyncHandleWrite(redisAsyncContext *ac); +void redisAsyncHandleTimeout(redisAsyncContext *ac); +void redisAsyncRead(redisAsyncContext *ac); +void redisAsyncWrite(redisAsyncContext *ac); + +/* Command functions for an async context. Write the command to the + * output buffer and register the provided callback. */ +int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap); +int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); +int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); +int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/async_private.h b/ext/hiredis-1.0.2/include/hiredis/async_private.h new file mode 100644 index 000000000..b9d23fffd --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/async_private.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_ASYNC_PRIVATE_H +#define __HIREDIS_ASYNC_PRIVATE_H + +#define _EL_ADD_READ(ctx) \ + do { \ + refreshTimeout(ctx); \ + if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \ + } while (0) +#define _EL_DEL_READ(ctx) do { \ + if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \ + } while(0) +#define _EL_ADD_WRITE(ctx) \ + do { \ + refreshTimeout(ctx); \ + if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \ + } while (0) +#define _EL_DEL_WRITE(ctx) do { \ + if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \ + } while(0) +#define _EL_CLEANUP(ctx) do { \ + if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \ + ctx->ev.cleanup = NULL; \ + } while(0); + +static inline void refreshTimeout(redisAsyncContext *ctx) { + #define REDIS_TIMER_ISSET(tvp) \ + (tvp && ((tvp)->tv_sec || (tvp)->tv_usec)) + + #define REDIS_EL_TIMER(ac, tvp) \ + if ((ac)->ev.scheduleTimer && REDIS_TIMER_ISSET(tvp)) { \ + (ac)->ev.scheduleTimer((ac)->ev.data, *(tvp)); \ + } + + if (ctx->c.flags & REDIS_CONNECTED) { + REDIS_EL_TIMER(ctx, ctx->c.command_timeout); + } else { + REDIS_EL_TIMER(ctx, ctx->c.connect_timeout); + } +} + +void __redisAsyncDisconnect(redisAsyncContext *ac); +void redisProcessCallbacks(redisAsyncContext *ac); + +#endif /* __HIREDIS_ASYNC_PRIVATE_H */ diff --git a/ext/hiredis-1.0.2/include/hiredis/dict.h b/ext/hiredis-1.0.2/include/hiredis/dict.h new file mode 100644 index 000000000..95fcd280e --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/dict.h @@ -0,0 +1,126 @@ +/* Hash table implementation. + * + * This file implements in memory hash tables with insert/del/replace/find/ + * get-random-element operations. Hash tables will auto resize if needed + * tables of power of two in size are used, collisions are handled by + * chaining. See the source code for more information... :) + * + * Copyright (c) 2006-2010, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DICT_H +#define __DICT_H + +#define DICT_OK 0 +#define DICT_ERR 1 + +/* Unused arguments generate annoying warnings... */ +#define DICT_NOTUSED(V) ((void) V) + +typedef struct dictEntry { + void *key; + void *val; + struct dictEntry *next; +} dictEntry; + +typedef struct dictType { + unsigned int (*hashFunction)(const void *key); + void *(*keyDup)(void *privdata, const void *key); + void *(*valDup)(void *privdata, const void *obj); + int (*keyCompare)(void *privdata, const void *key1, const void *key2); + void (*keyDestructor)(void *privdata, void *key); + void (*valDestructor)(void *privdata, void *obj); +} dictType; + +typedef struct dict { + dictEntry **table; + dictType *type; + unsigned long size; + unsigned long sizemask; + unsigned long used; + void *privdata; +} dict; + +typedef struct dictIterator { + dict *ht; + int index; + dictEntry *entry, *nextEntry; +} dictIterator; + +/* This is the initial size of every hash table */ +#define DICT_HT_INITIAL_SIZE 4 + +/* ------------------------------- Macros ------------------------------------*/ +#define dictFreeEntryVal(ht, entry) \ + if ((ht)->type->valDestructor) \ + (ht)->type->valDestructor((ht)->privdata, (entry)->val) + +#define dictSetHashVal(ht, entry, _val_) do { \ + if ((ht)->type->valDup) \ + entry->val = (ht)->type->valDup((ht)->privdata, _val_); \ + else \ + entry->val = (_val_); \ +} while(0) + +#define dictFreeEntryKey(ht, entry) \ + if ((ht)->type->keyDestructor) \ + (ht)->type->keyDestructor((ht)->privdata, (entry)->key) + +#define dictSetHashKey(ht, entry, _key_) do { \ + if ((ht)->type->keyDup) \ + entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ + else \ + entry->key = (_key_); \ +} while(0) + +#define dictCompareHashKeys(ht, key1, key2) \ + (((ht)->type->keyCompare) ? \ + (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ + (key1) == (key2)) + +#define dictHashKey(ht, key) (ht)->type->hashFunction(key) + +#define dictGetEntryKey(he) ((he)->key) +#define dictGetEntryVal(he) ((he)->val) +#define dictSlots(ht) ((ht)->size) +#define dictSize(ht) ((ht)->used) + +/* API */ +static unsigned int dictGenHashFunction(const unsigned char *buf, int len); +static dict *dictCreate(dictType *type, void *privDataPtr); +static int dictExpand(dict *ht, unsigned long size); +static int dictAdd(dict *ht, void *key, void *val); +static int dictReplace(dict *ht, void *key, void *val); +static int dictDelete(dict *ht, const void *key); +static void dictRelease(dict *ht); +static dictEntry * dictFind(dict *ht, const void *key); +static dictIterator *dictGetIterator(dict *ht); +static dictEntry *dictNext(dictIterator *iter); +static void dictReleaseIterator(dictIterator *iter); + +#endif /* __DICT_H */ diff --git a/ext/hiredis-1.0.2/include/hiredis/fmacros.h b/ext/hiredis-1.0.2/include/hiredis/fmacros.h new file mode 100644 index 000000000..3227faafd --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/fmacros.h @@ -0,0 +1,12 @@ +#ifndef __HIREDIS_FMACRO_H +#define __HIREDIS_FMACRO_H + +#define _XOPEN_SOURCE 600 +#define _POSIX_C_SOURCE 200112L + +#if defined(__APPLE__) && defined(__MACH__) +/* Enable TCP_KEEPALIVE */ +#define _DARWIN_C_SOURCE +#endif + +#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/hiredis.h b/ext/hiredis-1.0.2/include/hiredis/hiredis.h new file mode 100644 index 000000000..3bc46d992 --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/hiredis.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2014, Pieter Noordhuis + * Copyright (c) 2015, Matt Stancliff , + * Jan-Erik Rediger + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_H +#define __HIREDIS_H +#include "read.h" +#include /* for va_list */ +#ifndef _MSC_VER +#include /* for struct timeval */ +#else +struct timeval; /* forward declaration */ +typedef long long ssize_t; +#endif +#include /* uintXX_t, etc */ +#include "sds.h" /* for sds */ +#include "alloc.h" /* for allocation wrappers */ + +#define HIREDIS_MAJOR 1 +#define HIREDIS_MINOR 0 +#define HIREDIS_PATCH 2 +#define HIREDIS_SONAME 1.0.0 + +/* Connection type can be blocking or non-blocking and is set in the + * least significant bit of the flags field in redisContext. */ +#define REDIS_BLOCK 0x1 + +/* Connection may be disconnected before being free'd. The second bit + * in the flags field is set when the context is connected. */ +#define REDIS_CONNECTED 0x2 + +/* The async API might try to disconnect cleanly and flush the output + * buffer and read all subsequent replies before disconnecting. + * This flag means no new commands can come in and the connection + * should be terminated once all replies have been read. */ +#define REDIS_DISCONNECTING 0x4 + +/* Flag specific to the async API which means that the context should be clean + * up as soon as possible. */ +#define REDIS_FREEING 0x8 + +/* Flag that is set when an async callback is executed. */ +#define REDIS_IN_CALLBACK 0x10 + +/* Flag that is set when the async context has one or more subscriptions. */ +#define REDIS_SUBSCRIBED 0x20 + +/* Flag that is set when monitor mode is active */ +#define REDIS_MONITORING 0x40 + +/* Flag that is set when we should set SO_REUSEADDR before calling bind() */ +#define REDIS_REUSEADDR 0x80 + +/** + * Flag that indicates the user does not want the context to + * be automatically freed upon error + */ +#define REDIS_NO_AUTO_FREE 0x200 + +#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ + +/* number of times we retry to connect in the case of EADDRNOTAVAIL and + * SO_REUSEADDR is being used. */ +#define REDIS_CONNECT_RETRIES 10 + +/* Forward declarations for structs defined elsewhere */ +struct redisAsyncContext; +struct redisContext; + +/* RESP3 push helpers and callback prototypes */ +#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH) +typedef void (redisPushFn)(void *, void *); +typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *); + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the reply object returned by redisCommand() */ +typedef struct redisReply { + int type; /* REDIS_REPLY_* */ + long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ + double dval; /* The double when type is REDIS_REPLY_DOUBLE */ + size_t len; /* Length of string */ + char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING + REDIS_REPLY_VERB, and REDIS_REPLY_DOUBLE (in additional to dval). */ + char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null + terminated 3 character content type, such as "txt". */ + size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ + struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ +} redisReply; + +redisReader *redisReaderCreate(void); + +/* Function to free the reply objects hiredis returns by default. */ +void freeReplyObject(void *reply); + +/* Functions to format a command according to the protocol. */ +int redisvFormatCommand(char **target, const char *format, va_list ap); +int redisFormatCommand(char **target, const char *format, ...); +int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen); +int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen); +void redisFreeCommand(char *cmd); +void redisFreeSdsCommand(sds cmd); + +enum redisConnectionType { + REDIS_CONN_TCP, + REDIS_CONN_UNIX, + REDIS_CONN_USERFD +}; + +struct redisSsl; + +#define REDIS_OPT_NONBLOCK 0x01 +#define REDIS_OPT_REUSEADDR 0x02 + +/** + * Don't automatically free the async object on a connection failure, + * or other implicit conditions. Only free on an explicit call to disconnect() or free() + */ +#define REDIS_OPT_NOAUTOFREE 0x04 + +/* Don't automatically intercept and free RESP3 PUSH replies. */ +#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08 + +/* In Unix systems a file descriptor is a regular signed int, with -1 + * representing an invalid descriptor. In Windows it is a SOCKET + * (32- or 64-bit unsigned integer depending on the architecture), where + * all bits set (~0) is INVALID_SOCKET. */ +#ifndef _WIN32 +typedef int redisFD; +#define REDIS_INVALID_FD -1 +#else +#ifdef _WIN64 +typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */ +#else +typedef unsigned long redisFD; /* SOCKET = 32-bit UINT_PTR */ +#endif +#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */ +#endif + +typedef struct { + /* + * the type of connection to use. This also indicates which + * `endpoint` member field to use + */ + int type; + /* bit field of REDIS_OPT_xxx */ + int options; + /* timeout value for connect operation. If NULL, no timeout is used */ + const struct timeval *connect_timeout; + /* timeout value for commands. If NULL, no timeout is used. This can be + * updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */ + const struct timeval *command_timeout; + union { + /** use this field for tcp/ip connections */ + struct { + const char *source_addr; + const char *ip; + int port; + } tcp; + /** use this field for unix domain sockets */ + const char *unix_socket; + /** + * use this field to have hiredis operate an already-open + * file descriptor */ + redisFD fd; + } endpoint; + + /* Optional user defined data/destructor */ + void *privdata; + void (*free_privdata)(void *); + + /* A user defined PUSH message callback */ + redisPushFn *push_cb; + redisAsyncPushFn *async_push_cb; +} redisOptions; + +/** + * Helper macros to initialize options to their specified fields. + */ +#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \ + (opts)->type = REDIS_CONN_TCP; \ + (opts)->endpoint.tcp.ip = ip_; \ + (opts)->endpoint.tcp.port = port_; + +#define REDIS_OPTIONS_SET_UNIX(opts, path) \ + (opts)->type = REDIS_CONN_UNIX; \ + (opts)->endpoint.unix_socket = path; + +#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \ + (opts)->privdata = data; \ + (opts)->free_privdata = dtor; \ + +typedef struct redisContextFuncs { + void (*free_privctx)(void *); + void (*async_read)(struct redisAsyncContext *); + void (*async_write)(struct redisAsyncContext *); + ssize_t (*read)(struct redisContext *, char *, size_t); + ssize_t (*write)(struct redisContext *); +} redisContextFuncs; + +/* Context for a connection to Redis */ +typedef struct redisContext { + const redisContextFuncs *funcs; /* Function table */ + + int err; /* Error flags, 0 when there is no error */ + char errstr[128]; /* String representation of error when applicable */ + redisFD fd; + int flags; + char *obuf; /* Write buffer */ + redisReader *reader; /* Protocol reader */ + + enum redisConnectionType connection_type; + struct timeval *connect_timeout; + struct timeval *command_timeout; + + struct { + char *host; + char *source_addr; + int port; + } tcp; + + struct { + char *path; + } unix_sock; + + /* For non-blocking connect */ + struct sockadr *saddr; + size_t addrlen; + + /* Optional data and corresponding destructor users can use to provide + * context to a given redisContext. Not used by hiredis. */ + void *privdata; + void (*free_privdata)(void *); + + /* Internal context pointer presently used by hiredis to manage + * SSL connections. */ + void *privctx; + + /* An optional RESP3 PUSH handler */ + redisPushFn *push_cb; +} redisContext; + +redisContext *redisConnectWithOptions(const redisOptions *options); +redisContext *redisConnect(const char *ip, int port); +redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); +redisContext *redisConnectNonBlock(const char *ip, int port); +redisContext *redisConnectBindNonBlock(const char *ip, int port, + const char *source_addr); +redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, + const char *source_addr); +redisContext *redisConnectUnix(const char *path); +redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); +redisContext *redisConnectUnixNonBlock(const char *path); +redisContext *redisConnectFd(redisFD fd); + +/** + * Reconnect the given context using the saved information. + * + * This re-uses the exact same connect options as in the initial connection. + * host, ip (or path), timeout and bind address are reused, + * flags are used unmodified from the existing context. + * + * Returns REDIS_OK on successful connect or REDIS_ERR otherwise. + */ +int redisReconnect(redisContext *c); + +redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn); +int redisSetTimeout(redisContext *c, const struct timeval tv); +int redisEnableKeepAlive(redisContext *c); +void redisFree(redisContext *c); +redisFD redisFreeKeepFd(redisContext *c); +int redisBufferRead(redisContext *c); +int redisBufferWrite(redisContext *c, int *done); + +/* In a blocking context, this function first checks if there are unconsumed + * replies to return and returns one if so. Otherwise, it flushes the output + * buffer to the socket and reads until it has a reply. In a non-blocking + * context, it will return unconsumed replies until there are no more. */ +int redisGetReply(redisContext *c, void **reply); +int redisGetReplyFromReader(redisContext *c, void **reply); + +/* Write a formatted command to the output buffer. Use these functions in blocking mode + * to get a pipeline of commands. */ +int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len); + +/* Write a command to the output buffer. Use these functions in blocking mode + * to get a pipeline of commands. */ +int redisvAppendCommand(redisContext *c, const char *format, va_list ap); +int redisAppendCommand(redisContext *c, const char *format, ...); +int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); + +/* Issue a command to Redis. In a blocking context, it is identical to calling + * redisAppendCommand, followed by redisGetReply. The function will return + * NULL if there was an error in performing the request, otherwise it will + * return the reply. In a non-blocking context, it is identical to calling + * only redisAppendCommand and will always return NULL. */ +void *redisvCommand(redisContext *c, const char *format, va_list ap); +void *redisCommand(redisContext *c, const char *format, ...); +void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/hiredis_ssl.h b/ext/hiredis-1.0.2/include/hiredis/hiredis_ssl.h new file mode 100644 index 000000000..604efe0c1 --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/hiredis_ssl.h @@ -0,0 +1,127 @@ + +/* + * Copyright (c) 2019, Redis Labs + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_SSL_H +#define __HIREDIS_SSL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the underlying struct for SSL in ssl.h, which is not included to + * keep build dependencies short here. + */ +struct ssl_st; + +/* A wrapper around OpenSSL SSL_CTX to allow easy SSL use without directly + * calling OpenSSL. + */ +typedef struct redisSSLContext redisSSLContext; + +/** + * Initialization errors that redisCreateSSLContext() may return. + */ + +typedef enum { + REDIS_SSL_CTX_NONE = 0, /* No Error */ + REDIS_SSL_CTX_CREATE_FAILED, /* Failed to create OpenSSL SSL_CTX */ + REDIS_SSL_CTX_CERT_KEY_REQUIRED, /* Client cert and key must both be specified or skipped */ + REDIS_SSL_CTX_CA_CERT_LOAD_FAILED, /* Failed to load CA Certificate or CA Path */ + REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED, /* Failed to load client certificate */ + REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED /* Failed to load private key */ +} redisSSLContextError; + +/** + * Return the error message corresponding with the specified error code. + */ + +const char *redisSSLContextGetError(redisSSLContextError error); + +/** + * Helper function to initialize the OpenSSL library. + * + * OpenSSL requires one-time initialization before it can be used. Callers should + * call this function only once, and only if OpenSSL is not directly initialized + * elsewhere. + */ +int redisInitOpenSSL(void); + +/** + * Helper function to initialize an OpenSSL context that can be used + * to initiate SSL connections. + * + * cacert_filename is an optional name of a CA certificate/bundle file to load + * and use for validation. + * + * capath is an optional directory path where trusted CA certificate files are + * stored in an OpenSSL-compatible structure. + * + * cert_filename and private_key_filename are optional names of a client side + * certificate and private key files to use for authentication. They need to + * be both specified or omitted. + * + * server_name is an optional and will be used as a server name indication + * (SNI) TLS extension. + * + * If error is non-null, it will be populated in case the context creation fails + * (returning a NULL). + */ + +redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath, + const char *cert_filename, const char *private_key_filename, + const char *server_name, redisSSLContextError *error); + +/** + * Free a previously created OpenSSL context. + */ +void redisFreeSSLContext(redisSSLContext *redis_ssl_ctx); + +/** + * Initiate SSL on an existing redisContext. + * + * This is similar to redisInitiateSSL() but does not require the caller + * to directly interact with OpenSSL, and instead uses a redisSSLContext + * previously created using redisCreateSSLContext(). + */ + +int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx); + +/** + * Initiate SSL/TLS negotiation on a provided OpenSSL SSL object. + */ + +int redisInitiateSSL(redisContext *c, struct ssl_st *ssl); + +#ifdef __cplusplus +} +#endif + +#endif /* __HIREDIS_SSL_H */ diff --git a/ext/hiredis-1.0.2/include/hiredis/net.h b/ext/hiredis-1.0.2/include/hiredis/net.h new file mode 100644 index 000000000..9f43283a5 --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/net.h @@ -0,0 +1,56 @@ +/* Extracted from anet.c to work properly with Hiredis error reporting. + * + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2014, Pieter Noordhuis + * Copyright (c) 2015, Matt Stancliff , + * Jan-Erik Rediger + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __NET_H +#define __NET_H + +#include "hiredis.h" + +void redisNetClose(redisContext *c); +ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap); +ssize_t redisNetWrite(redisContext *c); + +int redisCheckSocketError(redisContext *c); +int redisContextSetTimeout(redisContext *c, const struct timeval tv); +int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); +int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, + const struct timeval *timeout, + const char *source_addr); +int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); +int redisKeepAlive(redisContext *c, int interval); +int redisCheckConnectDone(redisContext *c, int *completed); + +int redisSetTcpNoDelay(redisContext *c); + +#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/read.h b/ext/hiredis-1.0.2/include/hiredis/read.h new file mode 100644 index 000000000..2d74d77a5 --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/read.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __HIREDIS_READ_H +#define __HIREDIS_READ_H +#include /* for size_t */ + +#define REDIS_ERR -1 +#define REDIS_OK 0 + +/* When an error occurs, the err flag in a context is set to hold the type of + * error that occurred. REDIS_ERR_IO means there was an I/O error and you + * should use the "errno" variable to find out what is wrong. + * For other values, the "errstr" field will hold a description. */ +#define REDIS_ERR_IO 1 /* Error in read or write */ +#define REDIS_ERR_EOF 3 /* End of file */ +#define REDIS_ERR_PROTOCOL 4 /* Protocol error */ +#define REDIS_ERR_OOM 5 /* Out of memory */ +#define REDIS_ERR_TIMEOUT 6 /* Timed out */ +#define REDIS_ERR_OTHER 2 /* Everything else... */ + +#define REDIS_REPLY_STRING 1 +#define REDIS_REPLY_ARRAY 2 +#define REDIS_REPLY_INTEGER 3 +#define REDIS_REPLY_NIL 4 +#define REDIS_REPLY_STATUS 5 +#define REDIS_REPLY_ERROR 6 +#define REDIS_REPLY_DOUBLE 7 +#define REDIS_REPLY_BOOL 8 +#define REDIS_REPLY_MAP 9 +#define REDIS_REPLY_SET 10 +#define REDIS_REPLY_ATTR 11 +#define REDIS_REPLY_PUSH 12 +#define REDIS_REPLY_BIGNUM 13 +#define REDIS_REPLY_VERB 14 + +/* Default max unused reader buffer. */ +#define REDIS_READER_MAX_BUF (1024*16) + +/* Default multi-bulk element limit */ +#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct redisReadTask { + int type; + long long elements; /* number of elements in multibulk container */ + int idx; /* index in parent (array) object */ + void *obj; /* holds user-generated value for a read task */ + struct redisReadTask *parent; /* parent task */ + void *privdata; /* user-settable arbitrary field */ +} redisReadTask; + +typedef struct redisReplyObjectFunctions { + void *(*createString)(const redisReadTask*, char*, size_t); + void *(*createArray)(const redisReadTask*, size_t); + void *(*createInteger)(const redisReadTask*, long long); + void *(*createDouble)(const redisReadTask*, double, char*, size_t); + void *(*createNil)(const redisReadTask*); + void *(*createBool)(const redisReadTask*, int); + void (*freeObject)(void*); +} redisReplyObjectFunctions; + +typedef struct redisReader { + int err; /* Error flags, 0 when there is no error */ + char errstr[128]; /* String representation of error when applicable */ + + char *buf; /* Read buffer */ + size_t pos; /* Buffer cursor */ + size_t len; /* Buffer length */ + size_t maxbuf; /* Max length of unused buffer */ + long long maxelements; /* Max multi-bulk elements */ + + redisReadTask **task; + int tasks; + + int ridx; /* Index of current read task */ + void *reply; /* Temporary reply pointer */ + + redisReplyObjectFunctions *fn; + void *privdata; +} redisReader; + +/* Public API for the protocol parser. */ +redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn); +void redisReaderFree(redisReader *r); +int redisReaderFeed(redisReader *r, const char *buf, size_t len); +int redisReaderGetReply(redisReader *r, void **reply); + +#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) +#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply) +#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/sds.h b/ext/hiredis-1.0.2/include/hiredis/sds.h new file mode 100644 index 000000000..eda8833b5 --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/sds.h @@ -0,0 +1,278 @@ +/* SDSLib 2.0 -- A C dynamic strings library + * + * Copyright (c) 2006-2015, Salvatore Sanfilippo + * Copyright (c) 2015, Oran Agra + * Copyright (c) 2015, Redis Labs, Inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SDS_H +#define __SDS_H + +#define SDS_MAX_PREALLOC (1024*1024) +#ifdef _MSC_VER +#define __attribute__(x) +typedef long long ssize_t; +#define SSIZE_MAX (LLONG_MAX >> 1) +#endif + +#include +#include +#include + +typedef char *sds; + +/* Note: sdshdr5 is never used, we just access the flags byte directly. + * However is here to document the layout of type 5 SDS strings. */ +struct __attribute__ ((__packed__)) sdshdr5 { + unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr8 { + uint8_t len; /* used */ + uint8_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr16 { + uint16_t len; /* used */ + uint16_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr32 { + uint32_t len; /* used */ + uint32_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr64 { + uint64_t len; /* used */ + uint64_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; + +#define SDS_TYPE_5 0 +#define SDS_TYPE_8 1 +#define SDS_TYPE_16 2 +#define SDS_TYPE_32 3 +#define SDS_TYPE_64 4 +#define SDS_TYPE_MASK 7 +#define SDS_TYPE_BITS 3 +#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))); +#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) +#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) + +static inline size_t sdslen(const sds s) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + return SDS_TYPE_5_LEN(flags); + case SDS_TYPE_8: + return SDS_HDR(8,s)->len; + case SDS_TYPE_16: + return SDS_HDR(16,s)->len; + case SDS_TYPE_32: + return SDS_HDR(32,s)->len; + case SDS_TYPE_64: + return SDS_HDR(64,s)->len; + } + return 0; +} + +static inline size_t sdsavail(const sds s) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: { + return 0; + } + case SDS_TYPE_8: { + SDS_HDR_VAR(8,s); + return sh->alloc - sh->len; + } + case SDS_TYPE_16: { + SDS_HDR_VAR(16,s); + return sh->alloc - sh->len; + } + case SDS_TYPE_32: { + SDS_HDR_VAR(32,s); + return sh->alloc - sh->len; + } + case SDS_TYPE_64: { + SDS_HDR_VAR(64,s); + return sh->alloc - sh->len; + } + } + return 0; +} + +static inline void sdssetlen(sds s, size_t newlen) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + { + unsigned char *fp = ((unsigned char*)s)-1; + *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS)); + } + break; + case SDS_TYPE_8: + SDS_HDR(8,s)->len = (uint8_t)newlen; + break; + case SDS_TYPE_16: + SDS_HDR(16,s)->len = (uint16_t)newlen; + break; + case SDS_TYPE_32: + SDS_HDR(32,s)->len = (uint32_t)newlen; + break; + case SDS_TYPE_64: + SDS_HDR(64,s)->len = (uint64_t)newlen; + break; + } +} + +static inline void sdsinclen(sds s, size_t inc) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + { + unsigned char *fp = ((unsigned char*)s)-1; + unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc; + *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); + } + break; + case SDS_TYPE_8: + SDS_HDR(8,s)->len += (uint8_t)inc; + break; + case SDS_TYPE_16: + SDS_HDR(16,s)->len += (uint16_t)inc; + break; + case SDS_TYPE_32: + SDS_HDR(32,s)->len += (uint32_t)inc; + break; + case SDS_TYPE_64: + SDS_HDR(64,s)->len += (uint64_t)inc; + break; + } +} + +/* sdsalloc() = sdsavail() + sdslen() */ +static inline size_t sdsalloc(const sds s) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + return SDS_TYPE_5_LEN(flags); + case SDS_TYPE_8: + return SDS_HDR(8,s)->alloc; + case SDS_TYPE_16: + return SDS_HDR(16,s)->alloc; + case SDS_TYPE_32: + return SDS_HDR(32,s)->alloc; + case SDS_TYPE_64: + return SDS_HDR(64,s)->alloc; + } + return 0; +} + +static inline void sdssetalloc(sds s, size_t newlen) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + /* Nothing to do, this type has no total allocation info. */ + break; + case SDS_TYPE_8: + SDS_HDR(8,s)->alloc = (uint8_t)newlen; + break; + case SDS_TYPE_16: + SDS_HDR(16,s)->alloc = (uint16_t)newlen; + break; + case SDS_TYPE_32: + SDS_HDR(32,s)->alloc = (uint32_t)newlen; + break; + case SDS_TYPE_64: + SDS_HDR(64,s)->alloc = (uint64_t)newlen; + break; + } +} + +sds sdsnewlen(const void *init, size_t initlen); +sds sdsnew(const char *init); +sds sdsempty(void); +sds sdsdup(const sds s); +void sdsfree(sds s); +sds sdsgrowzero(sds s, size_t len); +sds sdscatlen(sds s, const void *t, size_t len); +sds sdscat(sds s, const char *t); +sds sdscatsds(sds s, const sds t); +sds sdscpylen(sds s, const char *t, size_t len); +sds sdscpy(sds s, const char *t); + +sds sdscatvprintf(sds s, const char *fmt, va_list ap); +#ifdef __GNUC__ +sds sdscatprintf(sds s, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#else +sds sdscatprintf(sds s, const char *fmt, ...); +#endif + +sds sdscatfmt(sds s, char const *fmt, ...); +sds sdstrim(sds s, const char *cset); +int sdsrange(sds s, ssize_t start, ssize_t end); +void sdsupdatelen(sds s); +void sdsclear(sds s); +int sdscmp(const sds s1, const sds s2); +sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); +void sdsfreesplitres(sds *tokens, int count); +void sdstolower(sds s); +void sdstoupper(sds s); +sds sdsfromlonglong(long long value); +sds sdscatrepr(sds s, const char *p, size_t len); +sds *sdssplitargs(const char *line, int *argc); +sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); +sds sdsjoin(char **argv, int argc, char *sep); +sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); + +/* Low level functions exposed to the user API */ +sds sdsMakeRoomFor(sds s, size_t addlen); +void sdsIncrLen(sds s, int incr); +sds sdsRemoveFreeSpace(sds s); +size_t sdsAllocSize(sds s); +void *sdsAllocPtr(sds s); + +/* Export the allocator used by SDS to the program using SDS. + * Sometimes the program SDS is linked to, may use a different set of + * allocators, but may want to allocate or free things that SDS will + * respectively free or allocate. */ +void *sds_malloc(size_t size); +void *sds_realloc(void *ptr, size_t size); +void sds_free(void *ptr); + +#ifdef REDIS_TEST +int sdsTest(int argc, char *argv[]); +#endif + +#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/sdsalloc.h b/ext/hiredis-1.0.2/include/hiredis/sdsalloc.h new file mode 100644 index 000000000..5538dd94c --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/sdsalloc.h @@ -0,0 +1,44 @@ +/* SDSLib 2.0 -- A C dynamic strings library + * + * Copyright (c) 2006-2015, Salvatore Sanfilippo + * Copyright (c) 2015, Oran Agra + * Copyright (c) 2015, Redis Labs, Inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* SDS allocator selection. + * + * This file is used in order to change the SDS allocator at compile time. + * Just define the following defines to what you want to use. Also add + * the include of your alternate allocator if needed (not needed in order + * to use the default libc allocator). */ + +#include "alloc.h" + +#define s_malloc hi_malloc +#define s_realloc hi_realloc +#define s_free hi_free diff --git a/ext/hiredis-1.0.2/include/hiredis/sockcompat.h b/ext/hiredis-1.0.2/include/hiredis/sockcompat.h new file mode 100644 index 000000000..85810e848 --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/sockcompat.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019, Marcus Geelnard + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SOCKCOMPAT_H +#define __SOCKCOMPAT_H + +#ifndef _WIN32 +/* For POSIX systems we use the standard BSD socket API. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +/* For Windows we use winsock. */ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */ +#include +#include +#include +#include + +#ifdef _MSC_VER +typedef long long ssize_t; +#endif + +/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */ +int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); +const char *win32_gai_strerror(int errcode); +void win32_freeaddrinfo(struct addrinfo *res); +SOCKET win32_socket(int domain, int type, int protocol); +int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp); +int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); +int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); +int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen); +int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen); +int win32_close(SOCKET fd); +ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags); +ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags); +typedef ULONG nfds_t; +int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout); + +#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION +#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res) +#undef gai_strerror +#define gai_strerror(errcode) win32_gai_strerror(errcode) +#define freeaddrinfo(res) win32_freeaddrinfo(res) +#define socket(domain, type, protocol) win32_socket(domain, type, protocol) +#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp) +#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen) +#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen) +#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen) +#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen) +#define close(fd) win32_close(fd) +#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags) +#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags) +#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout) +#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */ +#endif /* _WIN32 */ + +#endif /* __SOCKCOMPAT_H */ diff --git a/ext/hiredis-1.0.2/include/hiredis/win32.h b/ext/hiredis-1.0.2/include/hiredis/win32.h new file mode 100644 index 000000000..04289c696 --- /dev/null +++ b/ext/hiredis-1.0.2/include/hiredis/win32.h @@ -0,0 +1,56 @@ +#ifndef _WIN32_HELPER_INCLUDE +#define _WIN32_HELPER_INCLUDE +#ifdef _MSC_VER + +#include /* for struct timeval */ + +#ifndef inline +#define inline __inline +#endif + +#ifndef strcasecmp +#define strcasecmp stricmp +#endif + +#ifndef strncasecmp +#define strncasecmp strnicmp +#endif + +#ifndef va_copy +#define va_copy(d,s) ((d) = (s)) +#endif + +#ifndef snprintf +#define snprintf c99_snprintf + +__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) +{ + int count = -1; + + if (size != 0) + count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} + +__inline int c99_snprintf(char* str, size_t size, const char* format, ...) +{ + int count; + va_list ap; + + va_start(ap, format); + count = c99_vsnprintf(str, size, format, ap); + va_end(ap); + + return count; +} +#endif +#endif /* _MSC_VER */ + +#ifdef _WIN32 +#define strerror_r(errno,buf,len) strerror_s(buf,len,errno) +#endif /* _WIN32 */ + +#endif /* _WIN32_HELPER_INCLUDE */ diff --git a/ext/hiredis-1.0.2/lib/ubuntu22.04/libhiredis.a b/ext/hiredis-1.0.2/lib/ubuntu22.04/libhiredis.a new file mode 100644 index 0000000000000000000000000000000000000000..af1314f019db5c13386e6fd2a518de119e778076 GIT binary patch literal 557016 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%0A&MWMq<|8VRm#4mQM}4x6%& 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

7l613-v|i<9{@_j6;9K03 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

)hCpCaGX~rdX;bRU}hIgH;#mjPJqLNxAQU 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 R>127yk5W?1iVqea{|6hz^@bV6$1WI0beEH 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%0Axb}Yye~*0q7VbP@9()}7ui?%I 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

Set>}Q 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 OmN^g8T=Q%ylr<&9QZ2+f7F-nFGqXPR4FeJ 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 << '\t'; + std::cout << '\n'; + } +``` + +Result sets are immutable, so all iterators on results and rows are actually +`const_iterator`s. There are also `const_reverse_iterator` types, which +iterate backwards from `rbegin()` to `rend()` exclusive. + +All these iterator types provide one extra bit of convenience that you won't +normally find in C++ iterators: referential transparency. You don't need to +dereference them to get to the row or field they refer to. That is, instead +of `row->end()` you can also choose to say `row.end()`. Similarly, you +may prefer `field.c_str()` over `field->c_str()`. + +This becomes really helpful with the array-indexing operator. With regular +C++ iterators you would need ugly expressions like `(*row)[0]` or +`row->operator[](0)`. With the iterator types defined by the result and +row classes you can simply say `row[0]`. + + +Streaming rows +-------------- + +There's another way to go through the rows coming out of a query. It's +usually easier and faster, but there are drawbacks. + +**One,** you start getting rows before all the data has come in from the +database. That speeds things up, but what happens if you lose your network +connection while transferring the data? Your application may already have +processed some of the data before finding out that the rest isn't coming. If +that is a problem for your application, streaming may not be the right choice. + +**Two,** streaming only works for some types of query. The `stream()` function +wraps your query in a PostgreSQL `COPY` command, and `COPY` only supports a few +commands: `SELECT`, `VALUES`, `or an `INSERT`, `UPDATE`, or `DELETE` with a +`RETURNING` clause. See the `COPY` documentation here: +https://www.postgresql.org/docs/current/sql-copy.html + +**Three,** when you convert a field to a "view" type (such as +`std::string_view` or `std::basic_string_view`), the view points to +underlying data which only stays valid until you iterate to the next row or +exit the loop. So if you want to use that data for longer than a single +iteration of the streaming loop, you'll have to store it somewhere yourself. + +Now for the good news. Streaming does make it very easy to query data and loop +over it: + +```cxx + for (auto [id, name, x, y] : + tx.stream( + "SELECT id, name, x, y FROM point")) + process(id + 1, "point-" + name, x * 10.0, y * 10.0); +``` + +The conversion to C++ types (here `int`, `std::string_view`, and two `float`s) +is built into the function. You never even see `row` objects, `field` objects, +iterators, or conversion methods. You just put in your query and you receive +your data. diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/binary-data.md b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/binary-data.md new file mode 100644 index 000000000..20da8dc0c --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/binary-data.md @@ -0,0 +1,56 @@ +Binary data {#binary} +=========== + +The database has two ways of storing binary data: `BYTEA` is like a string, but +containing bytes rather than text characters. And _large objects_ are more +like a separate table containing binary objects. + +Generally you'll want to use `BYTEA` for reasonably-sized values, and large +objects for very large values. + +That's the database side. On the C++ side, in libpqxx, all binary data must be +either `std::basic_string` or `std::basic_string_view`; +or if you're building in C++20 or better, anything that's a block of +contiguous `std::byte` in memory. + +So for example, if you want to write a large object, you'd create a +`pqxx::blob` object. And you might use that to write data in the form of +`std::basic_string_view`. + +Your particular binary data may look different though. You may have it in a +`std::string`, or a `std::vector`, or a pointer to `char` +accompanied by a size (which could be signed or unsigned, and of any of a few +different widths). Sometimes that's your choice, or sometimes some other +library will dictate what form it takes. + +So long as it's _basically_ still a block of bytes though, you can use +`pqxx::binary_cast` to construct a `std::basic_string_view` from it. + +There are two forms of `binary_cast`. One takes a single argument that must +support `std::data()` and `std::size()`: + + std::string hi{"Hello binary world"}; + my_blob.write(pqxx::binary_cast(hi); + +The other takes a pointer and a size: + + char const greeting[] = "Hello binary world"; + char const *hi = greeting; + my_blob.write(pqxx::binary_cast(hi, sizeof(greeting))); + + +Caveats +------- + +There are some restrictions on `binary_cast` that you must be aware of. + +First, your data must of a type that gives us _bytes._ So: `char`, +`unsigned char`, `signed char`, `int8_t`, `uint8_t`, or of course `std::byte`. +You can't feed in a vector of `double`, or anything like that. + +Second, the data must be laid out as a contiguous block in memory. If there's +no `std::data()` implementation for your type, it's not suitable. + +Third, `binary_cast` only constructs something like a `std::string_view`. It +does not make a copy of your actual data. So, make sure that your data remains +alive and in the same place while you're using it. diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/datatypes.md b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/datatypes.md new file mode 100644 index 000000000..bc14c8b90 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/datatypes.md @@ -0,0 +1,373 @@ +Supporting additional data types {#datatypes} +================================ + +Communication with the database mostly happens in a text format. When you +include an integer value in a query, you use `to_string` to convert it to that +text format. When you get a query result field "as a float," it converts from +the text format to a floating-point type. These conversions are everywhere in +libpqxx. + +The conversion sydstem supports many built-in types, but it is also extensible. +You can "teach" libpqxx (in the scope of your own application) to convert +additional types of values to and from PostgreSQL's string format. + +This is massively useful, but it's not for the faint of heart. You'll need to +specialise some templates. And, **the API for doing this can change with any +major libpqxx release.** + + +Converting types +---------------- + +In your application, a conversion is driven entirely by a C++ type you specify. +The value's SQL type has nothing to do with it, nor is there anything in the +string that would identify its type. + +So, if you've SELECTed a 64-bit integer from the database, and you try to +convert it to a C++ "short," one of two things will happen: either the number +is small enough to fit in your `short` (and it just works), or else it throws a +conversion exception. + +Or, your database table might have a text column, but a given field may contain +a string that _looks_ just like a number. You can convert that value to an +integer type just fine. Or to a floating-point type. All that matters to the +conversion is the actual value, and the type. + +In some cases the templates for these conversions can tell the type from the +arguments you pass them: + + auto x = to_string(99); + +In other cases you may need to instantiate template explicitly: + + auto y = from_string("99"); + + +Supporting a new type +--------------------- + +Let's say you have some other SQL type which you want to be able to store in, +or retrieve from, the database. What would it take to support that? + +Sometimes you do not need _complete_ support. You might need a conversion _to_ +a string but not _from_ a string, for example. The conversion is defined at +compile time, so don't be too afraid to be incomplete. If you leave out one of +these steps, it's not going to crash at run time or mess up your data. The +worst that can happen is that your code won't build. + +So what do you need for a complete conversion? + +First off, of course, you need a C++ type. It may be your own, but it +doesn't have to be. It could be a type from a third-party library, or even one +from the standard library that libpqxx does not yet support. + +You also specialise the `pqxx::type_name` variable to specify the type's name. +This is important for all code which mentions your type in human-readable text, +such as error messages. + +Then, does your type have a built-in null value? You specialise the +`pqxx::nullness` template to specify the details. + +Finally, you specialise the `pqxx::string_traits` template. This is where you +define the actual conversions. + +Let's go through these steps one by one. + + +Your type +--------- + +You'll need a type for which the conversions are not yet defined, because the +C++ type is what determines the right conversion. One type, one set of +conversions. + +The type doesn't have to be one that you create. The conversion logic was +designed such that you can build it around any type. So you can just as +easily build a conversion for a type that's defined somewhere else. There's +no need to include any special methods or other members inside it. That's also +how libpqxx can support converting built-in types like `int`. + +By the way, if the type is an enum, you don't need to do any of this. Just +invoke the preprocessor macro `PQXX_DECLARE_ENUM_CONVERSION`, from the global +namespace near the top of your translation unit, and pass the type as an +argument. + +The library also provides specialisations for `std::optional`, +`std::shared_ptr`, and `std::unique_ptr`. If you have conversions for +`T`, you'll also have conversions for those. + + +Specialise `type_name` +---------------------- + +When errors happen during conversion, libpqxx will compose error messages for +the user. Sometimes these will include the name of the type that's being +converted. + +To tell libpqxx the name of each type, there's a template variable called +`pqxx::type_name`. For any given type `T`, it should have a specialisation +that provides that `T`'s human-readable name: + + namespace pqxx + { + template<> std::string const type_name{"T"}; + } + +(Yes, this means that you need to define something inside the pqxx namespace. +Future versions of libpqxx may move this into a separate namespace.) + +Define this early on in your translation unit, before any code that might cause +libpqxx to need the name. That way, the libpqxx code which needs to know the +type's name can see your definition. + + +Specialise `nullness` +--------------------- + +A struct template `pqxx::nullness` defines whether your type has a natural +"null value" built in. If so, it also provides member functions for producing +and recognising null values. + +The simplest scenario is also the most common: most types don't have a null +value built in. In that case, derive your nullness traits from +`pqxx::no_null`: + + namespace pqxx + { + template<> struct nullness : pqxx::no_null {}; + } + +(Here again you're defining this in the pqxx namespace.) + +If your type does have a natural null value, the definition gets a little more +complex: + + namespace pqxx + { + template<> struct nullness + { + static constexpr bool has_null{true}; + static constexpr bool always_null{false}; + + static bool is_null(T const &value) + { + // Return whether "value" is null. + return ...; + } + + [[nodiscard]] static T null() + { + // Return a null value. + return ...; + } + }; + } + +You may be wondering why there's a function to produce a null value, but also a +function to check whether a value is null. Why not just compare the value to +the result of `null()`? Because two null values may not be equal. `T` may +have several different null values. Or it may override the comparison +operator, similar to SQL where NULL is not equal to NULL. + +As a third case, your type may be one that _always_ represents a null value. +This is the case for `std::nullptr_t` and `std::nullopt_t`. In that case, you +set `nullness::always_null` to `true` (as well as `has_null` of course), +and you won't need to define any actual conversions. + + +Specialise `string_traits` +------------------------- + +This part is more work. (You can skip it for types that are _always_ null, +but those will be rare.) Specialise the `pqxx::string_traits` template: + + namespace pqxx + { + template<> struct string_traits + { + static T from_string(std::string_view text); + static zview to_buf(char *begin, char *end, T const &value); + static char *into_buf(char *begin, char *end, T const &value); + static std::size_t size_buffer(T const &value) noexcept; + }; + } + +You'll also need to write those member functions, or as many of them as needed +to get your code to build. + + +### `from_string` + +We start off simple: `from_string` parses a string as a value of `T`, and +returns that value. + +The string may not be zero-terminated; it's just the `string_view` from +beginning to end (exclusive). In your tests, cover cases where the string +does not end in a zero byte. + +It's perfectly possible that the string isn't actually a `T` value. Mistakes +happen. In that case, throw a `pqxx::conversion_error`. + +(Of course it's also possible that you run into some other error, so it's fine +to throw different exceptions as well. But when it's definitely "this is not +the right format for a `T`," throw `conversion_error`.) + + +### `to_buf` + +In this function, you convert a value of `T` into a string that the postgres +server will understand. + +The caller will provide you with a buffer where you can write the string, if +you need it: from `begin` to `end` exclusive. It's a half-open interval, so +don't access `*end`. + +If the buffer is insufficient for you to do the conversion, throw a +`pqxx::conversion_overrun`. It doesn't have to be exact: you can be a little +pessimistic and demand a bit more space than you need. Just be sure to throw +the exception if there's any risk of overrunning the buffer. + +You don't _have_ to use the buffer for this function though. For example, +`pqxx::string_traits::to_buf` returns a compile-time constant string and +ignores the buffer. + +Even if you do use the buffer, your string does not _have_ to start at the +beginning of the buffer. For example, the integer conversions start by writing +the _least_ significant digit to the _end_ of the buffer, and then writes the +more significant digits before it. It was just more convenient. + +Return a `pqxx::zview`. This is basically a `std::string_view`, but with one +difference: a `zview` guarantees that there will be a valid zero byte right +after the `string_view`. The zero byte is not counted as part of its size, but +it will be there. + +Expressed in code, this rule must hold: + + void invariant(zview z) + { + assert(z[std::size(z)] == 0); + } + +Make sure you write your trailing zero _before_ the `end`. If the trailing +zero doesn't fit in the buffer, then there's just not enough room to perform +the conversion. + +Beware of locales when converting. If you use standard library features like +`sprintf`, they may obey whatever locale is currently set on the system. That +means that a simple integer like 1000000 may come out as "1000000" on your +system, but as "1,000,000" on mine, or as "1.000.000" for somebody else, and on +an Indian system it may be "1,00,000". Values coming from or going to the +database should be in non-localised formats. You can use libpqxx functions for +those conversions: `pqxx::from_string`, `pqxx::to_string`, `pqxx::to_buf`. + + +### `into_buf` + +This is a stricter version of `to_buf`. All the same requirements apply, but +in addition you must write your string into the buffer provided, starting +_exactly_ at `begin`. + +That's why this function returns just a simple pointer: the address right +behind the trailing zero. If the caller wants to use the string, they can +find it at `begin`. If they want to write a different value into the rest of +the buffer, they can start at the location you returned. + + +### `size_buffer` + +Here you estimate how much buffer space you need for converting a `T` to a +string. Be precise if you can, but pessimistic if you must. It's usually +better to waste a few unnecessary bytes than to spend a lot of time computing +the exact buffer space you need. And failing the conversion because you +under-budgeted the buffer is worst of all. + +Include the trailing zero in the buffer size. If your `to_buf` takes more +space than just what's needed to store the result, include that too. + +Make `size_buffer` a `constexpr` function if you can. It can allow the caller +to allocate the buffer on the stack, with a size known at compile time. + + +Optional: Specialise `is_unquoted_safe` +--------------------------------------- + +When converting arrays or composite values to strings, libpqxx may need to +quote values and escape any special characters. This takes time. + +Some types though, such as integral or floating-point types, can never have +any special characters such as quotes, commas, or backslashes in their string +representations. In such cases, there's no need to quote or escape such values +in arrays or composite types. + +If your type is like that, you can tell libpqxx about this by defining: + + namespace pqxx + { + template<> inline constexpr bool is_unquoted_safe{true}; + } + +The code that converts this type of field to strings in an array or a composite +type can then use a simpler, more efficient variant of the code. It's always +safe to leave this out; it's _just_ an optimisation for when you're completely +sure that it's safe. + +Do not do this if a string representation of your type may contain a comma; +semicolon; parenthesis; brace; quote; backslash; newline; or any other +character that might need escaping. + + +Optional: Specialise `param_format` +----------------------------------- + +This one you don't generally need to worry about. Read on if you're writing a +type which represents raw binary data, or if you're writing a template where +_some specialisations_ may contain raw binary data. + +When you call parameterised statements, or prepared statements with parameters, +libpqxx needs to your parameters on to libpq, the underlying C-level PostgreSQL +client library. + +There are two formats for doing that: _text_ and _binary._ In the first, we +represent all values as strings, and the server then converts them into its own +internal binary representation. That's what the string conversions are all +about, and it's what we do for almost all types of parameters. + +But we do it differently when the parameter is a contiguous series of raw bytes +and the corresponding SQL type is `BYTEA`. There is a text format for those, +but we bypass it for efficiency. The server can use the binary data in the +exact same form, without any conversion or extra processing. The binary data +is also twice as compact during transport. + +(People sometimes ask why we can't just treat all types as binary. However the +general case isn't so clear-cut. The binary formats are not documented, there +are no guarantees that they will be platform-independent or that they will +remain stable, and there's no really solid way to detect when we might get the +format wrong. But also, the conversions aren't necessarily as straightforward +and efficient as they sound. So, for the general case, libpqxx sticks with the +text formats. Raw binary data alone stands out as a clear win.) + +Long story short, the machinery for passing parameters needs to know: is this +parameter a binary string, or not? In the normal case it can assume "no," and +that's what it does. The text format is always a safe choice; we just try to +use the binary format where it's faster. + +The `param_format` function template is what makes the decision. We specialise +it for types which may be binary strings, and use the default for all other +types. + +"Types which _may_ be binary"? You might think we know whether a type is a +binary type or not. But there are some complications with generic types. + +Templates like `std::shared_ptr`, `std::optional`, and so on act like +"wrappers" for another type. A `std::optional` is binary if `T` is binary. +Otherwise, it's not. If you're building support for a template of this nature, +you'll probably want to implement `param_format` for it. + +The decision to use binary format is made based on a given object, not +necessarily based on the type in general. Look at `std::variant`. If you have +a `std::variant` type which can hold an `int` or a binary string, is that a +binary parameter? We can't decide without knowing the individual object. + +Containers are another hard case. Should we pass `std::vector` in binary? +Even when `T` is a binary type, we don't currently have any way to pass an +array in binary format, so we always pass it as text. diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/escaping.md b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/escaping.md new file mode 100644 index 000000000..2ad9fe3db --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/escaping.md @@ -0,0 +1,74 @@ +String escaping {#escaping} +=============== + +Writing queries as strings is easy. But sometimes you need a variable in +there: `"SELECT id FROM user WHERE name = '" + name + "'"`. + +This is dangerous. See the bug? If `name` can contain quotes, you may have +an SQL injection vulnerability there, where users can enter nasty stuff like +"`.'; DROP TABLE user`". Or if you're lucky, it's just a nasty bug that you +discover when `name` happens to be "d'Arcy". + +So, you'll need to _escape_ the `name` before you insert it. This is where +quotes and other problematic characters are marked as "this is just a character +in the string, not the end of the string." There are +[several functions](@ref escaping-functions) in libpqxx to do this for you. + + +SQL injection +------------- + +To understand what SQL injection vulnerabilities are and why they should be +prevented, imagine you use the following SQL statement somewhere in your +program: + + TX.exec( + "SELECT number,amount " + "FROM accounts " + "WHERE allowed_to_see('" + userid + "','" + password + "')"); + +This shows a logged-in user important information on all accounts he is +authorized to view. The userid and password strings are variables entered +by the user himself. + +Now, if the user is actually an attacker who knows (or can guess) the +general shape of this SQL statement, imagine he enters the following +password: + + x') OR ('x' = 'x + +Does that make sense to you? Probably not. But if this is inserted into +the SQL string by the C++ code above, the query becomes: + + SELECT number,amount + FROM accounts + WHERE allowed_to_see('user','x') OR ('x' = 'x') + +Is this what you wanted to happen? Probably not! The neat `allowed_to_see()` +clause is completely circumvented by the "`OR ('x' = 'x')`" clause, which is +always `true`. Therefore, the attacker will get to see all accounts in the +database! + + +Using the esc functions +----------------------- + +Here's how you can fix the problem in the example above: + + TX.exec( + "SELECT number,amount " + "FROM accounts " + "WHERE allowed_to_see('" + TX.esc(userid) + "', " + "'" + TX.esc(password) + "')"); + +Now, the quotes embedded in the attacker's string will be neatly escaped so +they can't "break out" of the quoted SQL string they were meant to go into: + + SELECT number,amount + FROM accounts + WHERE allowed_to_see('user', 'x'') OR (''x'' = ''x') + +If you look carefully, you'll see that thanks to the added escape characters +(a single-quote is escaped in SQL by doubling it) all we get is a very +strange-looking password string--but not a change in the SQL statement. + diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/getting-started.md b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/getting-started.md new file mode 100644 index 000000000..1b87b881f --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/getting-started.md @@ -0,0 +1,142 @@ +Getting started {#getting-started} +=============== + +The most basic three types in libpqxx are the _connection_, the _transaction_, +and the _result_. + +They fit together as follows: +* You connect to the database by creating a `pqxx::connection` object (see + @ref connections). + +* You create a transaction object (see @ref transactions) operating on that + connection. You'll usually want the `pqxx::work` variety. + + Once you're done you call the transaction's `commit` function to make its + work final. If you don't call this, the work will be rolled back when the + transaction object is destroyed. + +* Until then, use the transaction's `exec`, `query_value`, and `stream` + functions (and variants) to execute SQL statements. You pass the statements + themselves in as simple strings. (See @ref streams for more about data + streaming). + +* Most of the `exec` functions return a `pqxx::result` object, which acts + as a standard container of rows: `pqxx::row`. + + Each row in a result, in turn, acts as a container of fields: `pqxx::field`. + See @ref accessing-results for more about results, rows, and fields. + +* Each field's data is stored internally as a text string, in a format defined + by PostgreSQL. You can convert field or row values using their `as()` and + `to()` member functions. + +* After you've closed the transaction, the connection is free to run a next + transaction. + +Here's a very basic example. It connects to the default database (you'll +need to have one set up), queries it for a very simple result, converts it to +an `int`, and prints it out. It also contains some basic error handling. + + #include + #include + + int main() + { + try + { + // Connect to the database. In practice we may have to pass some + // arguments to say where the database server is, and so on. + // The constructor parses options exactly like libpq's + // PQconnectdb/PQconnect, see: + // https://www.postgresql.org/docs/10/static/libpq-connect.html + pqxx::connection c; + + // Start a transaction. In libpqxx, you always work in one. + pqxx::work w(c); + + // work::exec1() executes a query returning a single row of data. + // We'll just ask the database to return the number 1 to us. + pqxx::row r = w.exec1("SELECT 1"); + + // Commit your transaction. If an exception occurred before this + // point, execution will have left the block, and the transaction will + // have been destroyed along the way. In that case, the failed + // transaction would implicitly abort instead of getting to this point. + w.commit(); + + // Look at the first and only field in the row, parse it as an integer, + // and print it. + // + // "r[0]" returns the first field, which has an "as<...>()" member + // function template to convert its contents from their string format + // to a type of your choice. + std::cout << r[0].as() << std::endl; + } + catch (std::exception const &e) + { + std::cerr << e.what() << std::endl; + return 1; + } + } + +This prints the number 1. Notice that you can keep the result object around +after you've closed the transaction or even the connection. There are +situations where you can't do it, but generally it's fine. If you're +interested: you can install your own callbacks for receiving error messages +from the database, and in that case you'll have to keep the connection object +alive. But otherwise, it's nice to be able to "fire and forget" your +connection and deal with the data. + +You can also convert an entire row to a series of C++-side types in one go, +using the @c as member function on the row: + + pqxx::connection c; + pqxx::work w(c); + pqxx::row r = w.exec1("SELECT 1, 2, 'Hello'"); + auto [one, two, hello] = r.as(); + std::cout << (one + two) << ' ' << std::strlen(hello) << std::endl; + +Here's a slightly more complicated example. It takes an argument from the +command line and retrieves a string with that value. The interesting part is +that it uses the escaping-and-quoting function `quote` to embed this +string value in SQL safely. It also reads the result field's value as a +plain C-style string using its `c_str` function. + + #include + #include + #include + + int main(int argc, char *argv[]) + { + try + { + if (!argv[1]) throw std::runtime_error("Give me a string!"); + + pqxx::connection c; + pqxx::work w(c); + + // work::exec() returns a full result set, which can consist of any + // number of rows. + pqxx::result r = w.exec("SELECT " + w.quote(argv[1])); + + // End our transaction here. We can still use the result afterwards. + w.commit(); + + // Print the first field of the first row. Read it as a C string, + // just like std::string::c_str() does. + std::cout << r[0][0].c_str() << std::endl; + } + catch (std::exception const &e) + { + std::cerr << e.what() << std::endl; + return 1; + } + } + +You can find more about converting field values to native types, or +converting values to strings for use with libpqxx, under +@ref stringconversion. More about getting to the rows and fields of a +result is under @ref accessing-results. + +If you want to handle exceptions thrown by libpqxx in more detail, for +example to print the SQL contents of a query that failed, see @ref exception. diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/mainpage.md b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/mainpage.md new file mode 100644 index 000000000..5d4b8f9b2 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/mainpage.md @@ -0,0 +1,28 @@ +libpqxx {#mainpage} +======= + +@version 7.7.3 +@author Jeroen T. Vermeulen +@see http://pqxx.org +@see https://github.com/jtv/libpqxx + +Welcome to libpqxx, the C++ API to the PostgreSQL database management system. + +Compiling this package requires PostgreSQL to be installed -- including the +C headers for client development. The library builds on top of PostgreSQL's +standard C API, libpq. The libpq headers are not needed to compile client +programs, however. + +For a quick introduction to installing and using libpqxx, see the README.md +file. The latest information can be found at http://pqxx.org/ + + +Some links that should help you find your bearings: +* @ref getting-started +* @ref thread-safety +* @ref connections +* @ref transactions +* @ref escaping +* @ref performance +* @ref transactor +* @ref datatypes diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/parameters.md b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/parameters.md new file mode 100644 index 000000000..7ac792025 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/parameters.md @@ -0,0 +1,90 @@ +Statement parameters {#parameters} +==================== + +When you execute a prepared statement (see @ref prepared), or a parameterised +statement (using functions like `pqxx::connection::exec_params`), you may write +special _placeholders_ in the query text. They look like `$1`, `$2`, and so +on. + +If you execute the query and pass parameter values, the call will respectively +substitute the first where it finds `$1`, the second where it finds `$2`, et +cetera. + +Doing this saves you work. If you don't use statement parameters, you'll need +to quote and escape your values (see `connection::quote()` and friends) as you +insert them into your query as literal values. + +Or if you forget to do that, you leave yourself open to horrible +[SQL injection attacks](https://xkcd.com/327/). Trust me, I was born in a town +whose name started with an apostrophe! + +Statement parameters save you this work. With these parameters you can pass +your values as-is, and they will go across the wire to the database in a safe +format. + +In some cases it may even be faster! When a parameter represents binary data +(as in the SQL `BYTEA` type), libpqxx will send it directly as binary, which is +a bit more efficient. If you insert the binary data directly in your query +text, your CPU will have some extra work to do, converting the data into a text +format, escaping it, and adding quotes. + + +Dynamic parameter lists +----------------------- + +In rare cases you may just not know how many parameters you'll pass into your +statement when you call it. + +For these situations, have a look at `params`. It lets you compose your +parameters list on the fly, even add whole ranges of parameters at a time. + +You can pass a `params` into your statement as a normal parameter. It will +fill in all the parameter values it contains into that position of the +statement's overall parameter list. + +So if you call your statement passing a regular parameter `a`, a +`params` containing just a parameter `b`, and another regular parameter `c`, +then your call will pass parameters `a`, `b`, and `c`. Or if the params object +is empty, it will pass just `a` and `c`. If the params object contains `x` and +`y`, your call will pass `a, x, y, c`. + +You can mix static and dynamic parameters freely. Don't go overboard though: +complexity is where bugs happen! + + +Generating placeholders +----------------------- + +If your code gets particularly complex, it may sometimes happen that it becomes +hard to track which parameter value belongs with which placeholder. Did you +intend to pass this numeric value as `$7`, or as `$8`? The answer may depend +on an `if` that happened earlier in a different function. + +(Generally if things get that complex, it's a good idea to look for simpler +solutions. But especially when performance matters, sometimes you can't avoid +complexity like that.) + +There's a little helper class called `placeholders`. You can use it as a +counter which produces those placeholder strings, `$1`, `$2`, `$3`, et cetera. +When you start generating a complex statement, you can create both a `params` +and a `placeholders`: + + pqxx::params values; + pqxx::placeholders name; + +Let's say you've got some complex code to generate the conditions for an SQL +"WHERE" clause. You'll generally want to do these things close together in +your, so that you don't accidentally update one part and forget another: + + if (extra_clause) + { + // Extend the query text, using the current placeholder. + query += " AND x = " + name.get(); + // Add the parameter value. + values.append(my_x); + // Move on to the next placeholder value. + name.next(); + } + +Depending on the starting value of `name`, this might add to `query` a fragment +like "` AND x = $3`" or "` AND x = $5`". diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/performance.md b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/performance.md new file mode 100644 index 000000000..6c403684f --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/performance.md @@ -0,0 +1,24 @@ +Performance features {#performance} +==================== + +If your program's database interaction is not as efficient as it needs to be, +the first place to look is usually the SQL you're executing. But libpqxx +has a few specialized features to help you squeeze more performance out +of how you issue commands and retrieve data: + +* @ref streams. Use these as a faster way to transfer data between your + code and the database. +* `std::string_view` and `pqxx::zview`. In places where traditional C++ worked + with `std::string`, see whether `std::string_view` or `pqxx::zview` will + do. Of course that means that you'll have to look at the data's lifetime + more carefully, but it'll save the computer a lot of copying. +* @ref prepared. These can be executed many times without the server + parsing and planning them anew each time. They also save you having to + escape string parameters. +* `pqxx::pipeline` lets you send queries to the database in batches, and + continue other processing while they are executing. +* `pqxx::connecting` lets you start setting up a database connection, but + without blocking the thread. + +As always of course, don't risk the quality of your code for optimizations +that you don't need! diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/prepared-statement.md b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/prepared-statement.md new file mode 100644 index 000000000..5193866a6 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/prepared-statement.md @@ -0,0 +1,125 @@ +Prepared statements {#prepared} +=================== + +Prepared statements are SQL queries that you define once and then invoke +as many times as you like, typically with varying parameters. It's basically +a function that you can define ad hoc. + +If you have an SQL statement that you're going to execute many times in +quick succession, it may be more efficient to prepare it once and reuse it. +This saves the database backend the effort of parsing complex SQL and +figuring out an efficient execution plan. Another nice side effect is that +you don't need to worry about escaping parameters. Some corporate coding +standards require all SQL parameters to be passed in this way, to reduce the +risk of programmer mistakes leaving room for SQL injections. + + +Preparing a statement +--------------------- + +You create a prepared statement by preparing it on the connection (using the +`pqxx::connection::prepare` functions), passing an identifier and its SQL text. + +The identifier is the name by which the prepared statement will be known; it +should consist of ASCII letters, digits, and underscores only, and start with +an ASCII letter. The name is case-sensitive. + +```cxx + void prepare_my_statement(pqxx::connection &c) + { + c.prepare( + "my_statement", + "SELECT * FROM Employee WHERE name = 'Xavier'"); + } +``` + +Once you've done this, you'll be able to call `my_statement` from any +transaction you execute on the same connection. For this, use the +`pqxx::transaction_base::exec_prepared` functions. + +```cxx + pqxx::result execute_my_statement(pqxx::transaction_base &t) + { + return t.exec_prepared("my_statement"); + } +``` + + +Parameters +---------- + +Did I mention that prepared statements can have parameters? The query text +can contain `$1`, `$2` etc. as placeholders for parameter values that you +will provide when you invoke the prepared satement. + +See @ref parameters for more about this. And here's a simple example of +preparing a statement and invoking it with parameters: + +```cxx + void prepare_find(pqxx::connection &c) + { + // Prepare a statement called "find" that looks for employees with a + // given name (parameter 1) whose salary exceeds a given number + // (parameter 2). + c.prepare( + "find", + "SELECT * FROM Employee WHERE name = $1 AND salary > $2"); + } +``` + +This example looks up the prepared statement "find," passes `name` and +`min_salary` as parameters, and invokes the statement with those values: + +```cxx + pqxx::result execute_find( + pqxx::transaction_base &t, std::string name, int min_salary) + { + return t.exec_prepared("find", name, min_salary); + } +``` + + +A special prepared statement +---------------------------- + +There is one special case: the _nameless_ prepared statement. You may prepare +a statement without a name, i.e. whose name is an empty string. The unnamed +statement can be redefined at any time, without un-preparing it first. + + +Performance note +---------------- + +Don't assume that using prepared statements will speed up your application. +There are cases where prepared statements are actually slower than plain SQL. + +The reason is that the backend can often produce a better execution plan when +it knows the statement's actual parameter values. + +For example, say you've got a web application and you're querying for users +with status "inactive" who have email addresses in a given domain name X. If +X is a very popular provider, the best way for the database engine to plan the +query may be to list the inactive users first and then filter for the email +addresses you're looking for. But in other cases, it may be much faster to +find matching email addresses first and then see which of their owners are +"inactive." A prepared statement must be planned to fit either case, but a +direct query will be optimised based on table statistics, partial indexes, etc. + + +Zero bytes +---------- + +@warning Beware of "nul" bytes! + +Any string you pass as a parameter will end at the _first char with value +zero._ If you pass a string that contains a zero byte, the last byte in the +value will be the one just before the zero. + +So, if you need a zero byte in a string, consider that it's really a _binary +string,_ which is not the same thing as a text string. SQL represents binary +data as the `BYTEA` type, or in binary large objects ("blobs"). + +In libpqxx, you represent binary data as a range of `std::byte`. They must be +contiguous in memory, so that libpqxx can pass pointers to the underlying C +library. So you might use `std::basic_string`, or +`std::basic_string_view`, or `std::vector`. diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/streams.md b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/streams.md new file mode 100644 index 000000000..3df4d6126 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/streams.md @@ -0,0 +1,107 @@ +Streams {#streams} +======= + +Most of the time it's fine to retrieve data from the database using `SELECT` +queries, and store data using `INSERT`. But for those cases where efficiency +matters, there are two classes to help you do this better: `stream_from` and +`stream_to`. They're less flexible than SQL queries, and there's the risk of +losing your connection while you're in mid-stream, but you get some speed and +memory efficiencies in return. + +Both stream classes do data conversion for you: `stream_from` receives values +from the database in PostgreSQL's text format, and converts them to the C++ +types you specify. Likewise, `stream_to` converts C++ values you provide to +PostgreSQL's text format for transfer. (On its end, the database of course +converts values to and from their SQL types.) + + +Null values +----------- + +So how do you deal with nulls? It depends on the C++ type you're using. Some +types may have a built-in null value. For instance, if you have a +`char const *` value and you convert it to an SQL string, then converting a +`nullptr` will produce a NULL SQL value. + +But what do you do about C++ types which don't have a built-in null value, such +as `int`? The trick is to wrap it in `std::optional`. The difference between +`int` and `std::optional` is that the former always has an `int` value, +and the latter doesn't have to. + +Actually it's not just `std::optional`. You can do the same thing with +`std::unique_ptr` or `std::shared_ptr`. A smart pointer is less efficient than +`std::optional` in most situations because they allocate their value on the +heap, but sometimes that's what you want in order to save moving or copying +large values around. + +This part is not generic though. It won't work with just any smart-pointer +type, just the ones which are explicitly supported: `shared_ptr` and +`unique_ptr`. If you really need to, you can build support for additional +wrappers and smart pointers by copying the implementation patterns from the +existing smart-pointer support. + + +stream\_from +------------ + +Use `stream_from` to read data directly from the database. It's faster than +the transaction's `exec` functions if the result contains enough rows. But +also, you won't need to keep your full result set in memory. That can really +matter with larger data sets. + +And, you can start processing your data right after the first row of data comes +in from the server. With `exec()` you need to wait to receive all data, and +then you begin processing. With `stream_from` you can be processing data on +the client side while the server is still sending you the rest. + +You don't actually need to create a `stream_from` object yourself, though you +can. Two shorthand functions, @ref pqxx::transaction_base::stream +and @ref pqxx::transaction_base::for_each, can create the streams for you with +a minimum of overhead. + +Not all kinds of queries will work in a stream. Internally the streams make +use of PostgreSQL's `COPY` command, so see the PostgreSQL documentation for +`COPY` for the exact limitations. Basic `SELECT` and `UPDATE ... RETURNING` +queries should just work. + +As you read a row, the stream converts its fields to a tuple type containing +the value types you ask for: + + auto stream pqxx::stream_from::query( + tx, "SELECT name, points FROM score"); + std::tuple row; + while (stream >> row) + process(row); + stream.complete(); + +As the stream reads each row, it converts that row's data into your tuple, +goes through your loop body, and then promptly forgets that row's data. This +means you can easily process more data than will fit in memory. + + +stream\_to +---------- + +Use `stream_to` to write data directly to a database table. This saves you +having to perform an `INSERT` for every row, and so it can be significantly +faster if you want to insert more than just one or two rows at a time. + +As with `stream_from`, you can specify the table and the columns, and not much +else. You insert tuple-like objects of your choice: + + pqxx::stream_to stream{ + tx, + "score", + std::vector{"name", "points"}}; + for (auto const &entry: scores) + stream << entry; + stream.complete(); + +Each row is processed as you provide it, and not retained in memory after that. + +The call to `complete()` is more important here than it is for `stream_from`. +It's a lot like a "commit" or "abort" at the end of a transaction. If you omit +it, it will be done automatically during the stream's destructor. But since +destructors can't throw exceptions, any failures at that stage won't be visible +in your code. So, always call `complete()` on a `stream_to` to close it off +properly! diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/thread-safety.md b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/thread-safety.md new file mode 100644 index 000000000..07c7f9984 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/share/doc/libpqxx/thread-safety.md @@ -0,0 +1,29 @@ +Thread safety {#thread-safety} +============= + +This library does not contain any locking code to protect objects against +simultaneous modification in multi-threaded programs. Therefore it is up +to you, the user of the library, to ensure that your threaded client +programs perform no conflicting operations concurrently. + +Most of the time this isn't hard. Result sets are immutable, so you can +share them between threads without problem. The main rule is: + +@li Treat a connection, together with any and all objects related to it, as +a "world" of its own. You should generally make sure that the same "world" +is never accessed by another thread while you're doing anything non-const +in there. + +That means: don't issue a query on a transaction while you're also opening +a subtransaction, don't access a cursor while you may also be committing, +and so on. + +In particular, cursors are tricky. It's easy to perform a non-const +operation without noticing. So, if you're going to share cursors or +cursor-related objects between threads, lock very conservatively! + +Use `pqxx::describe_thread_safety` to find out at runtime what level of +thread safety is implemented in your build and version of libpqxx. It +returns a `pqxx::thread_safety_model` describing what you can and cannot rely +on. A command-line utility `tools/pqxxthreadsafety` prints out the same +information. diff --git a/ext/libpqxx-7.7.3/libpqxx.pc.in b/ext/libpqxx-7.7.3/libpqxx.pc.in new file mode 100644 index 000000000..eb7dcff49 --- /dev/null +++ b/ext/libpqxx-7.7.3/libpqxx.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libpqxx +Description: C++ client API for the PostgreSQL database management system. +Version: @VERSION@ +Libs: -L${libdir} -lpqxx +Cflags: -I${includedir} diff --git a/ext/libpqxx-7.7.3/requirements.json b/ext/libpqxx-7.7.3/requirements.json new file mode 100644 index 000000000..28f8f8ad1 --- /dev/null +++ b/ext/libpqxx-7.7.3/requirements.json @@ -0,0 +1,9 @@ +{ + "description": "Minimum versions needed of various things.", + "c++": "17", + "libpq": "9.6", + "postgresql": "9.6", + "gcc": "8", + "clang": "11", + "msvc": "2019" +} diff --git a/ext/libpqxx-7.7.3/src/CMakeLists.txt b/ext/libpqxx-7.7.3/src/CMakeLists.txt new file mode 100644 index 000000000..1d697ab7c --- /dev/null +++ b/ext/libpqxx-7.7.3/src/CMakeLists.txt @@ -0,0 +1,91 @@ +if(NOT PostgreSQL_FOUND) + if(POLICY CMP0074) + cmake_policy(PUSH) + # CMP0074 is `OLD` by `cmake_minimum_required(VERSION 3.7)`, + # sets `NEW` to enable support CMake variable `PostgreSQL_ROOT`. + cmake_policy(SET CMP0074 NEW) + endif() + + find_package(PostgreSQL REQUIRED) + + if(POLICY CMP0074) + cmake_policy(POP) + endif() +endif() + +# When setting up the include paths, mention the binary tree's include +# directory *before* the source tree's include directory. If the source tree +# happens to contain autoconf-generated config headers, we should still prefer +# the ones in the binary tree. +macro(library_target_setup tgt) + target_include_directories(${tgt} + PUBLIC + $ + $ + $ + PRIVATE + ${PostgreSQL_INCLUDE_DIRS} + ) + target_link_libraries(${tgt} PRIVATE ${PostgreSQL_LIBRARIES}) + if(WIN32) + target_link_libraries(${tgt} PUBLIC wsock32 ws2_32) + endif() + install(TARGETS ${tgt} EXPORT libpqxx-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + + get_target_property(name ${tgt} NAME) + get_target_property(output_name ${tgt} OUTPUT_NAME) + if(NOT CMAKE_HOST_WIN32) + # Create library symlink + get_target_property(target_type ${tgt} TYPE) + if(target_type STREQUAL "SHARED_LIBRARY") + set(library_prefix ${CMAKE_SHARED_LIBRARY_PREFIX}) + set(library_suffix ${CMAKE_SHARED_LIBRARY_SUFFIX}) + elseif(target_type STREQUAL "STATIC_LIBRARY") + set(library_prefix ${CMAKE_STATIC_LIBRARY_PREFIX}) + set(library_suffix ${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() + + list(APPEND noop_command "${CMAKE_COMMAND}" "-E" "true") + list(APPEND create_symlink_command "${CMAKE_COMMAND}" "-E" "create_symlink" "${library_prefix}${output_name}${library_suffix}" "${library_prefix}${name}${library_suffix}") + # `add_custom_command()` does nothing if the `OUTPUT_NAME` and `NAME` + # properties are equal, otherwise it creates library symlink. + add_custom_command(TARGET ${tgt} POST_BUILD + COMMAND "$,${noop_command},${create_symlink_command}>" + VERBATIM + COMMAND_EXPAND_LISTS + ) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${library_prefix}${name}${library_suffix} + DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + endif() +endmacro() + +file(GLOB CXX_SOURCES *.cxx) + +add_library(pqxx ${CXX_SOURCES}) + +get_target_property(pqxx_target_type pqxx TYPE) +if(pqxx_target_type STREQUAL "SHARED_LIBRARY") + target_compile_definitions(pqxx PUBLIC PQXX_SHARED) +endif() + +set_target_properties( + pqxx PROPERTIES + OUTPUT_NAME $,pqxx,pqxx-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}> +) +library_target_setup(pqxx) + +# install pkg-config file +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix \${prefix}) +set(libdir "\${prefix}/${CMAKE_INSTALL_LIBDIR}") +set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") +set(VERSION ${PROJECT_VERSION}) +configure_file(${PROJECT_SOURCE_DIR}/libpqxx.pc.in ${PROJECT_BINARY_DIR}/libpqxx.pc) +install(FILES ${PROJECT_BINARY_DIR}/libpqxx.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig +) diff --git a/ext/libpqxx-7.7.3/src/Makefile.am b/ext/libpqxx-7.7.3/src/Makefile.am new file mode 100644 index 000000000..dfd520941 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/Makefile.am @@ -0,0 +1,44 @@ +lib_LTLIBRARIES = libpqxx.la +libpqxx_la_SOURCES = \ + array.cxx \ + binarystring.cxx \ + blob.cxx \ + connection.cxx \ + cursor.cxx \ + encodings.cxx \ + errorhandler.cxx \ + except.cxx \ + field.cxx \ + largeobject.cxx \ + notification.cxx \ + params.cxx \ + pipeline.cxx \ + result.cxx \ + robusttransaction.cxx \ + sql_cursor.cxx \ + strconv.cxx \ + stream_from.cxx \ + stream_to.cxx \ + subtransaction.cxx \ + time.cxx \ + transaction.cxx \ + transaction_base.cxx \ + row.cxx \ + util.cxx \ + version.cxx \ + wait.cxx + +libpqxx_version = -release $(PQXX_ABI) + +libpqxx_la_LDFLAGS = $(libpqxx_version) \ + -rpath $(libdir) \ + ${POSTGRES_LIB} + +AM_CPPFLAGS = \ + -I$(top_srcdir)/include -I$(top_builddir)/include ${POSTGRES_INCLUDE} + +# Override automatically generated list of default includes. It contains only +# unnecessary entries, and incorrectly mentions include/pqxx directly. +DEFAULT_INCLUDES= + +MAINTAINERCLEANFILES=Makefile.in diff --git a/ext/libpqxx-7.7.3/src/Makefile.in b/ext/libpqxx-7.7.3/src/Makefile.in new file mode 100644 index 000000000..3c0152727 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/Makefile.in @@ -0,0 +1,809 @@ +# Makefile.in generated by automake 1.16.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/config/m4/libtool.m4 \ + $(top_srcdir)/config/m4/ltoptions.m4 \ + $(top_srcdir)/config/m4/ltsugar.m4 \ + $(top_srcdir)/config/m4/ltversion.m4 \ + $(top_srcdir)/config/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/include/pqxx/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libpqxx_la_LIBADD = +am_libpqxx_la_OBJECTS = array.lo binarystring.lo blob.lo connection.lo \ + cursor.lo encodings.lo errorhandler.lo except.lo field.lo \ + largeobject.lo notification.lo params.lo pipeline.lo result.lo \ + robusttransaction.lo sql_cursor.lo strconv.lo stream_from.lo \ + stream_to.lo subtransaction.lo time.lo transaction.lo \ + transaction_base.lo row.lo util.lo version.lo wait.lo +libpqxx_la_OBJECTS = $(am_libpqxx_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libpqxx_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(libpqxx_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/array.Plo \ + ./$(DEPDIR)/binarystring.Plo ./$(DEPDIR)/blob.Plo \ + ./$(DEPDIR)/connection.Plo ./$(DEPDIR)/cursor.Plo \ + ./$(DEPDIR)/encodings.Plo ./$(DEPDIR)/errorhandler.Plo \ + ./$(DEPDIR)/except.Plo ./$(DEPDIR)/field.Plo \ + ./$(DEPDIR)/largeobject.Plo ./$(DEPDIR)/notification.Plo \ + ./$(DEPDIR)/params.Plo ./$(DEPDIR)/pipeline.Plo \ + ./$(DEPDIR)/result.Plo ./$(DEPDIR)/robusttransaction.Plo \ + ./$(DEPDIR)/row.Plo ./$(DEPDIR)/sql_cursor.Plo \ + ./$(DEPDIR)/strconv.Plo ./$(DEPDIR)/stream_from.Plo \ + ./$(DEPDIR)/stream_to.Plo ./$(DEPDIR)/subtransaction.Plo \ + ./$(DEPDIR)/time.Plo ./$(DEPDIR)/transaction.Plo \ + ./$(DEPDIR)/transaction_base.Plo ./$(DEPDIR)/util.Plo \ + ./$(DEPDIR)/version.Plo ./$(DEPDIR)/wait.Plo +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libpqxx_la_SOURCES) +DIST_SOURCES = $(libpqxx_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/config/depcomp \ + $(top_srcdir)/config/mkinstalldirs +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PG_CONFIG = @PG_CONFIG@ +PKG_CONFIG = @PKG_CONFIG@ +POSTGRES_INCLUDE = @POSTGRES_INCLUDE@ +PQXXVERSION = @PQXXVERSION@ +PQXX_ABI = @PQXX_ABI@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_postgres_lib = @with_postgres_lib@ +lib_LTLIBRARIES = libpqxx.la +libpqxx_la_SOURCES = \ + array.cxx \ + binarystring.cxx \ + blob.cxx \ + connection.cxx \ + cursor.cxx \ + encodings.cxx \ + errorhandler.cxx \ + except.cxx \ + field.cxx \ + largeobject.cxx \ + notification.cxx \ + params.cxx \ + pipeline.cxx \ + result.cxx \ + robusttransaction.cxx \ + sql_cursor.cxx \ + strconv.cxx \ + stream_from.cxx \ + stream_to.cxx \ + subtransaction.cxx \ + time.cxx \ + transaction.cxx \ + transaction_base.cxx \ + row.cxx \ + util.cxx \ + version.cxx \ + wait.cxx + +libpqxx_version = -release $(PQXX_ABI) +libpqxx_la_LDFLAGS = $(libpqxx_version) \ + -rpath $(libdir) \ + ${POSTGRES_LIB} + +AM_CPPFLAGS = \ + -I$(top_srcdir)/include -I$(top_builddir)/include ${POSTGRES_INCLUDE} + + +# Override automatically generated list of default includes. It contains only +# unnecessary entries, and incorrectly mentions include/pqxx directly. +DEFAULT_INCLUDES = +MAINTAINERCLEANFILES = Makefile.in +all: all-am + +.SUFFIXES: +.SUFFIXES: .cxx .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libpqxx.la: $(libpqxx_la_OBJECTS) $(libpqxx_la_DEPENDENCIES) $(EXTRA_libpqxx_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libpqxx_la_LINK) -rpath $(libdir) $(libpqxx_la_OBJECTS) $(libpqxx_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/binarystring.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blob.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cursor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encodings.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errorhandler.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/except.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/field.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/largeobject.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notification.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/params.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pipeline.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/result.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/robusttransaction.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/row.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sql_cursor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strconv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stream_from.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stream_to.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subtransaction.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transaction.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transaction_base.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wait.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cxx.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cxx.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cxx.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/array.Plo + -rm -f ./$(DEPDIR)/binarystring.Plo + -rm -f ./$(DEPDIR)/blob.Plo + -rm -f ./$(DEPDIR)/connection.Plo + -rm -f ./$(DEPDIR)/cursor.Plo + -rm -f ./$(DEPDIR)/encodings.Plo + -rm -f ./$(DEPDIR)/errorhandler.Plo + -rm -f ./$(DEPDIR)/except.Plo + -rm -f ./$(DEPDIR)/field.Plo + -rm -f ./$(DEPDIR)/largeobject.Plo + -rm -f ./$(DEPDIR)/notification.Plo + -rm -f ./$(DEPDIR)/params.Plo + -rm -f ./$(DEPDIR)/pipeline.Plo + -rm -f ./$(DEPDIR)/result.Plo + -rm -f ./$(DEPDIR)/robusttransaction.Plo + -rm -f ./$(DEPDIR)/row.Plo + -rm -f ./$(DEPDIR)/sql_cursor.Plo + -rm -f ./$(DEPDIR)/strconv.Plo + -rm -f ./$(DEPDIR)/stream_from.Plo + -rm -f ./$(DEPDIR)/stream_to.Plo + -rm -f ./$(DEPDIR)/subtransaction.Plo + -rm -f ./$(DEPDIR)/time.Plo + -rm -f ./$(DEPDIR)/transaction.Plo + -rm -f ./$(DEPDIR)/transaction_base.Plo + -rm -f ./$(DEPDIR)/util.Plo + -rm -f ./$(DEPDIR)/version.Plo + -rm -f ./$(DEPDIR)/wait.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/array.Plo + -rm -f ./$(DEPDIR)/binarystring.Plo + -rm -f ./$(DEPDIR)/blob.Plo + -rm -f ./$(DEPDIR)/connection.Plo + -rm -f ./$(DEPDIR)/cursor.Plo + -rm -f ./$(DEPDIR)/encodings.Plo + -rm -f ./$(DEPDIR)/errorhandler.Plo + -rm -f ./$(DEPDIR)/except.Plo + -rm -f ./$(DEPDIR)/field.Plo + -rm -f ./$(DEPDIR)/largeobject.Plo + -rm -f ./$(DEPDIR)/notification.Plo + -rm -f ./$(DEPDIR)/params.Plo + -rm -f ./$(DEPDIR)/pipeline.Plo + -rm -f ./$(DEPDIR)/result.Plo + -rm -f ./$(DEPDIR)/robusttransaction.Plo + -rm -f ./$(DEPDIR)/row.Plo + -rm -f ./$(DEPDIR)/sql_cursor.Plo + -rm -f ./$(DEPDIR)/strconv.Plo + -rm -f ./$(DEPDIR)/stream_from.Plo + -rm -f ./$(DEPDIR)/stream_to.Plo + -rm -f ./$(DEPDIR)/subtransaction.Plo + -rm -f ./$(DEPDIR)/time.Plo + -rm -f ./$(DEPDIR)/transaction.Plo + -rm -f ./$(DEPDIR)/transaction_base.Plo + -rm -f ./$(DEPDIR)/util.Plo + -rm -f ./$(DEPDIR)/version.Plo + -rm -f ./$(DEPDIR)/wait.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ext/libpqxx-7.7.3/src/array.cxx b/ext/libpqxx-7.7.3/src/array.cxx new file mode 100644 index 000000000..e35aaddce --- /dev/null +++ b/ext/libpqxx-7.7.3/src/array.cxx @@ -0,0 +1,240 @@ +/** Handling of SQL arrays. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include +#include +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/array.hxx" +#include "pqxx/except.hxx" +#include "pqxx/internal/array-composite.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/strconv.hxx" +#include "pqxx/util.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace pqxx +{ +/// Scan to next glyph in the buffer. Assumes there is one. +[[nodiscard]] std::string::size_type +array_parser::scan_glyph(std::string::size_type pos) const +{ + return m_scan(std::data(m_input), std::size(m_input), pos); +} + + +/// Scan to next glyph in a substring. Assumes there is one. +std::string::size_type array_parser::scan_glyph( + std::string::size_type pos, std::string::size_type end) const +{ + return m_scan(std::data(m_input), end, pos); +} + + +/// Find the end of a single-quoted SQL string in an SQL array. +/** Call this while pointed at the opening quote. + * + * Returns the offset of the first character after the closing quote. + */ +std::string::size_type array_parser::scan_single_quoted_string() const +{ + assert(m_input[m_pos] == '\''); + auto const sz{std::size(m_input)}; + auto here{pqxx::internal::find_char<'\\', '\''>(m_scan, m_input, m_pos + 1)}; + while (here < sz) + { + char const c{m_input[here]}; + // Consume the slash or quote that we found. + ++here; + if (c == '\'') + { + // Single quote. + + // At end? + if (here >= sz) + return here; + + // SQL escapes single quotes by doubling them. Terrible idea, but it's + // what we have. Inspect the next character to find out whether this + // is the closing quote, or an escaped one inside the string. + if (m_input[here] != '\'') + return here; + // Check against embedded "'" byte in a multichar byte. If we do have a + // multibyte char, then we're still out of the string. + if (scan_glyph(here, sz) > here + 1) + PQXX_UNLIKELY return here; + + // We have a second quote. Consume it as well. + ++here; + } + else + { + assert(c == '\\'); + // Backslash escape. Skip ahead by one more character. + here = scan_glyph(here, sz); + } + // Race on to the next quote or backslash. + here = pqxx::internal::find_char<'\\', '\''>(m_scan, m_input, here); + } + throw argument_error{internal::concat("Null byte in SQL string: ", m_input)}; +} + + +/// Parse a single-quoted SQL string: un-quote it and un-escape it. +std::string +array_parser::parse_single_quoted_string(std::string::size_type end) const +{ + std::string output; + // Maximum output size is same as the input size, minus the opening and + // closing quotes. In the worst case, the real number could be half that. + // Usually it'll be a pretty close estimate. + output.reserve(end - m_pos - 2); + // XXX: find_char<'\\', '\''>(). + for (auto here = m_pos + 1, next = scan_glyph(here, end); here < end - 1; + here = next, next = scan_glyph(here, end)) + { + if (next - here == 1 and (m_input[here] == '\'' or m_input[here] == '\\')) + { + // Skip escape. (Performance-wise, we bet that these are relatively + // rare.) + PQXX_UNLIKELY + here = next; + next = scan_glyph(here, end); + } + + output.append(std::data(m_input) + here, std::data(m_input) + next); + } + + return output; +} + + +/// Find the end of a double-quoted SQL string in an SQL array. +std::string::size_type array_parser::scan_double_quoted_string() const +{ + return pqxx::internal::scan_double_quoted_string( + std::data(m_input), std::size(m_input), m_pos, m_scan); +} + + +/// Parse a double-quoted SQL string: un-quote it and un-escape it. +std::string +array_parser::parse_double_quoted_string(std::string::size_type end) const +{ + return pqxx::internal::parse_double_quoted_string( + std::data(m_input), end, m_pos, m_scan); +} + + +/// Find the end of an unquoted string in an SQL array. +/** Assumes UTF-8 or an ASCII-superset single-byte encoding. + */ +std::string::size_type array_parser::scan_unquoted_string() const +{ + return pqxx::internal::scan_unquoted_string<',', ';', '}'>( + std::data(m_input), std::size(m_input), m_pos, m_scan); +} + + +/// Parse an unquoted SQL string. +/** Here, the special unquoted value NULL means a null value, not a string + * that happens to spell "NULL". + */ +std::string +array_parser::parse_unquoted_string(std::string::size_type end) const +{ + return pqxx::internal::parse_unquoted_string( + std::data(m_input), end, m_pos, m_scan); +} + + +array_parser::array_parser( + std::string_view input, internal::encoding_group enc) : + m_input(input), m_scan(internal::get_glyph_scanner(enc)) +{} + + +std::pair array_parser::get_next() +{ + std::string value; + + if (m_pos >= std::size(m_input)) + return std::make_pair(juncture::done, value); + + juncture found; + std::string::size_type end; + + if (scan_glyph(m_pos) - m_pos > 1) + { + // Non-ASCII unquoted string. + end = scan_unquoted_string(); + value = parse_unquoted_string(end); + found = juncture::string_value; + } + else + switch (m_input[m_pos]) + { + case '\0': throw failure{"Unexpected zero byte in array."}; + case '{': + found = juncture::row_start; + end = scan_glyph(m_pos); + break; + case '}': + found = juncture::row_end; + end = scan_glyph(m_pos); + break; + case '\'': + found = juncture::string_value; + end = scan_single_quoted_string(); + value = parse_single_quoted_string(end); + break; + case '"': + found = juncture::string_value; + end = scan_double_quoted_string(); + value = parse_double_quoted_string(end); + break; + default: + end = scan_unquoted_string(); + value = parse_unquoted_string(end); + if (value == "NULL") + { + // In this one situation, as a special case, NULL means a null field, + // not a string that happens to spell "NULL". + value.clear(); + found = juncture::null_value; + } + else + { + // The normal case: we just parsed an unquoted string. The value is + // what we need. + PQXX_LIKELY + found = juncture::string_value; + } + break; + } + + // Skip a trailing field separator, if present. + if (end < std::size(m_input)) + { + auto next{scan_glyph(end)}; + if (next - end == 1 and (m_input[end] == ',' or m_input[end] == ';')) + PQXX_UNLIKELY + end = next; + } + + m_pos = end; + return std::make_pair(found, value); +} +} // namespace pqxx diff --git a/ext/libpqxx-7.7.3/src/binarystring.cxx b/ext/libpqxx-7.7.3/src/binarystring.cxx new file mode 100644 index 000000000..936437006 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/binarystring.cxx @@ -0,0 +1,108 @@ +/** Implementation of bytea (binary string) conversions. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include +#include +#include +#include +#include + +extern "C" +{ +#include +} + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/binarystring.hxx" +#include "pqxx/field.hxx" +#include "pqxx/strconv.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace +{ +/// Copy data to a heap-allocated buffer. +std::shared_ptr + PQXX_COLD copy_to_buffer(void const *data, std::size_t len) +{ + void *const output{malloc(len + 1)}; + if (output == nullptr) + throw std::bad_alloc{}; + static_cast(output)[len] = '\0'; + memcpy(static_cast(output), data, len); + return {static_cast(output), std::free}; +} +} // namespace + + +PQXX_COLD pqxx::binarystring::binarystring(field const &F) +{ + unsigned char const *data{ + reinterpret_cast(F.c_str())}; + m_buf = + std::shared_ptr{PQunescapeBytea(data, &m_size), PQfreemem}; + if (m_buf == nullptr) + throw std::bad_alloc{}; +} + + +pqxx::binarystring::binarystring(std::string_view s) : + m_buf{copy_to_buffer(std::data(s), std::size(s))}, m_size{std::size(s)} +{} + + +pqxx::binarystring::binarystring(void const *binary_data, std::size_t len) : + m_buf{copy_to_buffer(binary_data, len)}, m_size{len} +{} + + +bool pqxx::binarystring::operator==(binarystring const &rhs) const noexcept +{ + return (std::size(rhs) == size()) and + (std::memcmp(data(), std::data(rhs), size()) == 0); +} + + +pqxx::binarystring & +pqxx::binarystring::operator=(binarystring const &rhs) = default; + +PQXX_COLD pqxx::binarystring::const_reference +pqxx::binarystring::at(size_type n) const +{ + if (n >= m_size) + { + if (m_size == 0) + throw std::out_of_range{"Accessing empty binarystring"}; + throw std::out_of_range{ + "binarystring index out of range: " + to_string(n) + + " (should be below " + to_string(m_size) + ")"}; + } + return data()[n]; +} + + +PQXX_COLD void pqxx::binarystring::swap(binarystring &rhs) +{ + m_buf.swap(rhs.m_buf); + + // This part very obviously can't go wrong, so do it last + auto const s{m_size}; + m_size = rhs.m_size; + rhs.m_size = s; +} + + +std::string pqxx::binarystring::str() const +{ + return std::string{get(), m_size}; +} diff --git a/ext/libpqxx-7.7.3/src/blob.cxx b/ext/libpqxx-7.7.3/src/blob.cxx new file mode 100644 index 000000000..1492d9107 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/blob.cxx @@ -0,0 +1,337 @@ +#include "pqxx-source.hxx" + +#include +#include +#include + +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/blob.hxx" +#include "pqxx/except.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/gates/connection-largeobject.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace +{ +constexpr int INV_WRITE{0x00020000}, INV_READ{0x00040000}; +} // namespace + + +pqxx::internal::pq::PGconn * +pqxx::blob::raw_conn(pqxx::connection *conn) noexcept +{ + pqxx::internal::gate::connection_largeobject gate{*conn}; + return gate.raw_connection(); +} + + +pqxx::internal::pq::PGconn * +pqxx::blob::raw_conn(pqxx::dbtransaction const &tx) noexcept +{ + return raw_conn(&tx.conn()); +} + + +std::string pqxx::blob::errmsg(connection const *conn) +{ + pqxx::internal::gate::const_connection_largeobject gate{*conn}; + return gate.error_message(); +} + + +pqxx::blob pqxx::blob::open_internal(dbtransaction &tx, oid id, int mode) +{ + auto &conn{tx.conn()}; + int fd{lo_open(raw_conn(&conn), id, mode)}; + if (fd == -1) + throw pqxx::failure{internal::concat( + "Could not open binary large object ", id, ": ", errmsg(&conn))}; + return {conn, fd}; +} + + +pqxx::oid pqxx::blob::create(dbtransaction &tx, oid id) +{ + oid actual_id{lo_create(raw_conn(tx), id)}; + if (actual_id == 0) + throw failure{internal::concat( + "Could not create binary large object: ", errmsg(&tx.conn()))}; + return actual_id; +} + + +void pqxx::blob::remove(dbtransaction &tx, oid id) +{ + if (id == 0) + throw usage_error{"Trying to delete binary large object without an ID."}; + if (lo_unlink(raw_conn(tx), id) == -1) + throw failure{internal::concat( + "Could not delete large object ", id, ": ", errmsg(&tx.conn()))}; +} + + +pqxx::blob pqxx::blob::open_r(dbtransaction &tx, oid id) +{ + return open_internal(tx, id, INV_READ); +} + + +pqxx::blob pqxx::blob::open_w(dbtransaction &tx, oid id) +{ + return open_internal(tx, id, INV_WRITE); +} + + +pqxx::blob pqxx::blob::open_rw(dbtransaction &tx, oid id) +{ + return open_internal(tx, id, INV_READ | INV_WRITE); +} + + +pqxx::blob::blob(blob &&other) : + m_conn{std::exchange(other.m_conn, nullptr)}, + m_fd{std::exchange(other.m_fd, -1)} +{} + + +pqxx::blob &pqxx::blob::operator=(blob &&other) +{ + if (m_fd != -1) + lo_close(raw_conn(m_conn), m_fd); + m_conn = std::exchange(other.m_conn, nullptr); + m_fd = std::exchange(other.m_fd, -1); + return *this; +} + + +pqxx::blob::~blob() +{ + try + { + close(); + } + catch (std::exception const &e) + { + if (m_conn != nullptr) + PQXX_UNLIKELY + m_conn->process_notice(internal::concat( + "Failure while closing binary large object: ", e.what(), "\n")); + } +} + + +void pqxx::blob::close() +{ + if (m_fd != -1) + { + lo_close(raw_conn(m_conn), m_fd); + m_fd = -1; + m_conn = nullptr; + } +} + + +std::size_t pqxx::blob::raw_read(std::byte buf[], std::size_t size) +{ + if (m_conn == nullptr) + throw usage_error{"Attempt to read from a closed binary large object."}; + if (size > chunk_limit) + throw range_error{ + "Reads from a binary large object must be less than 2 GB at once."}; + auto data{reinterpret_cast(buf)}; + int received{lo_read(raw_conn(m_conn), m_fd, data, size)}; + if (received < 0) + throw failure{ + internal::concat("Could not read from binary large object: ", errmsg())}; + return static_cast(received); +} + + +std::size_t +pqxx::blob::read(std::basic_string &buf, std::size_t size) +{ + buf.resize(size); + auto const received{raw_read(std::data(buf), size)}; + buf.resize(received); + return static_cast(received); +} + + +void pqxx::blob::raw_write(std::byte const buf[], std::size_t size) +{ + if (m_conn == nullptr) + throw usage_error{"Attempt to write to a closed binary large object."}; + if (size > chunk_limit) + throw range_error{ + "Writes to a binary large object must be less than 2 GB at once."}; + auto ptr{reinterpret_cast(buf)}; + int written{lo_write(raw_conn(m_conn), m_fd, ptr, size)}; + if (written < 0) + throw failure{ + internal::concat("Write to binary large object failed: ", errmsg())}; +} + + +void pqxx::blob::resize(std::int64_t size) +{ + if (m_conn == nullptr) + throw usage_error{"Attempt to resize a closed binary large object."}; + if (lo_truncate64(raw_conn(m_conn), m_fd, size) < 0) + throw failure{ + internal::concat("Binary large object truncation failed: ", errmsg())}; +} + + +std::int64_t pqxx::blob::tell() const +{ + if (m_conn == nullptr) + throw usage_error{"Attempt to tell() a closed binary large object."}; + std::int64_t offset{lo_tell64(raw_conn(m_conn), m_fd)}; + if (offset < 0) + throw failure{internal::concat( + "Error reading binary large object position: ", errmsg())}; + return offset; +} + + +std::int64_t pqxx::blob::seek(std::int64_t offset, int whence) +{ + if (m_conn == nullptr) + throw usage_error{"Attempt to seek() a closed binary large object."}; + std::int64_t seek_result{lo_lseek64(raw_conn(m_conn), m_fd, offset, whence)}; + if (seek_result < 0) + throw failure{internal::concat( + "Error during seek on binary large object: ", errmsg())}; + return seek_result; +} + + +std::int64_t pqxx::blob::seek_abs(std::int64_t offset) +{ + return this->seek(offset, SEEK_SET); +} + + +std::int64_t pqxx::blob::seek_rel(std::int64_t offset) +{ + return this->seek(offset, SEEK_CUR); +} + + +std::int64_t pqxx::blob::seek_end(std::int64_t offset) +{ + return this->seek(offset, SEEK_END); +} + + +pqxx::oid pqxx::blob::from_buf( + dbtransaction &tx, std::basic_string_view data, oid id) +{ + oid actual_id{create(tx, id)}; + try + { + open_w(tx, actual_id).write(data); + } + catch (std::exception const &) + { + try + { + remove(tx, id); + } + catch (std::exception const &e) + { + try + { + tx.conn().process_notice(internal::concat( + "Could not clean up partially created large object ", id, ": ", + e.what())); + } + catch (std::exception const &) + {} + } + throw; + } + return actual_id; +} + + +void pqxx::blob::append_from_buf( + dbtransaction &tx, std::basic_string_view data, oid id) +{ + if (std::size(data) > chunk_limit) + throw range_error{ + "Writes to a binary large object must be less than 2 GB at once."}; + blob b{open_w(tx, id)}; + b.seek_end(); + b.write(data); +} + + +void pqxx::blob::to_buf( + dbtransaction &tx, oid id, std::basic_string &buf, + std::size_t max_size) +{ + open_r(tx, id).read(buf, max_size); +} + + +std::size_t pqxx::blob::append_to_buf( + dbtransaction &tx, oid id, std::int64_t offset, + std::basic_string &buf, std::size_t append_max) +{ + if (append_max > chunk_limit) + throw range_error{ + "Reads from a binary large object must be less than 2 GB at once."}; + auto b{open_r(tx, id)}; + b.seek_abs(offset); + auto const org_size{std::size(buf)}; + buf.resize(org_size + append_max); + try + { + auto here{reinterpret_cast(std::data(buf) + org_size)}; + auto chunk{static_cast( + lo_read(b.raw_conn(b.m_conn), b.m_fd, here, append_max))}; + buf.resize(org_size + chunk); + return chunk; + } + catch (std::exception const &) + { + buf.resize(org_size); + throw; + } +} + + +pqxx::oid pqxx::blob::from_file(dbtransaction &tx, char const path[]) +{ + auto id{lo_import(raw_conn(tx), path)}; + if (id == 0) + throw failure{internal::concat( + "Could not import '", path, "' as a binary large object: ", errmsg(tx))}; + return id; +} + + +pqxx::oid pqxx::blob::from_file(dbtransaction &tx, char const path[], oid id) +{ + auto actual_id{lo_import_with_oid(raw_conn(tx), path, id)}; + if (actual_id == 0) + throw failure{internal::concat( + "Could not import '", path, "' as binary large object ", id, ": ", + errmsg(tx))}; + return actual_id; +} + + +void pqxx::blob::to_file(dbtransaction &tx, oid id, char const path[]) +{ + if (lo_export(raw_conn(tx), id, path) < 0) + throw failure{internal::concat( + "Could not export binary large object ", id, " to file '", path, + "': ", errmsg(tx))}; +} diff --git a/ext/libpqxx-7.7.3/src/connection.cxx b/ext/libpqxx-7.7.3/src/connection.cxx new file mode 100644 index 000000000..bef8c79aa --- /dev/null +++ b/ext/libpqxx-7.7.3/src/connection.cxx @@ -0,0 +1,1274 @@ +/** Implementation of the pqxx::connection class. + * + * pqxx::connection encapsulates a connection to a database. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// For fcntl(). +#if __has_include() +# include +#endif +#if __has_include() +# include +#endif + +// For ioctlsocket(). +#if defined(_WIN32) && __has_include() +# include +#endif + +extern "C" +{ +#include +} + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/binarystring.hxx" +#include "pqxx/internal/wait.hxx" +#include "pqxx/nontransaction.hxx" +#include "pqxx/notification.hxx" +#include "pqxx/pipeline.hxx" +#include "pqxx/result.hxx" +#include "pqxx/strconv.hxx" +#include "pqxx/transaction.hxx" + +#include "pqxx/internal/gates/errorhandler-connection.hxx" +#include "pqxx/internal/gates/result-connection.hxx" +#include "pqxx/internal/gates/result-creation.hxx" + +#include "pqxx/internal/header-post.hxx" + + +extern "C" +{ + // The PQnoticeProcessor that receives an error or warning from libpq and + // sends it to the appropriate connection for processing. + void pqxx_notice_processor(void *conn, char const *msg) noexcept + { + reinterpret_cast(conn)->process_notice(msg); + } + + + // There's no way in libpq to disable a connection's notice processor. So, + // set an inert one to get the same effect. + void inert_notice_processor(void *, char const *) noexcept {} +} // extern "C" + +using namespace std::literals; + +std::string PQXX_COLD +pqxx::encrypt_password(char const user[], char const password[]) +{ + std::unique_ptr> p{ + PQencryptPassword(password, user), PQfreemem}; + return {p.get()}; +} + + +pqxx::connection::connection(connection &&rhs) : + m_conn{rhs.m_conn}, m_unique_id{rhs.m_unique_id} +{ + rhs.check_movable(); + rhs.m_conn = nullptr; +} + + +pqxx::connection::connection( + connection::connect_mode, zview connection_string) : + m_conn{PQconnectStart(connection_string.c_str())} +{ + if (m_conn == nullptr) + throw std::bad_alloc{}; + if (status() == CONNECTION_BAD) + throw pqxx::broken_connection{PQerrorMessage(m_conn)}; +} + + +std::pair pqxx::connection::poll_connect() +{ + switch (PQconnectPoll(m_conn)) + { + case PGRES_POLLING_FAILED: + throw pqxx::broken_connection{PQerrorMessage(m_conn)}; + case PGRES_POLLING_READING: return std::make_pair(true, false); + case PGRES_POLLING_WRITING: return std::make_pair(false, true); + case PGRES_POLLING_OK: + if (not is_open()) + throw pqxx::broken_connection{PQerrorMessage(m_conn)}; + return std::make_pair(false, false); + case PGRES_POLLING_ACTIVE: + throw internal_error{ + "Nonblocking connection poll returned obsolete 'active' state."}; + default: + throw internal_error{ + "Nonblocking connection poll returned unknown value."}; + } +} + +void pqxx::connection::complete_init() +{ + if (m_conn == nullptr) + throw std::bad_alloc{}; + try + { + if (not is_open()) + throw broken_connection{PQerrorMessage(m_conn)}; + + set_up_state(); + } + catch (std::exception const &) + { + PQfinish(m_conn); + m_conn = nullptr; + throw; + } +} + + +void pqxx::connection::init(char const options[]) +{ + m_conn = PQconnectdb(options); + complete_init(); +} + + +void pqxx::connection::init(char const *params[], char const *values[]) +{ + m_conn = PQconnectdbParams(params, values, 0); + complete_init(); +} + + +void pqxx::connection::check_movable() const +{ + if (m_trans) + throw pqxx::usage_error{"Moving a connection with a transaction open."}; + if (not std::empty(m_errorhandlers)) + throw pqxx::usage_error{ + "Moving a connection with error handlers registered."}; + if (not std::empty(m_receivers)) + throw pqxx::usage_error{ + "Moving a connection with notification receivers registered."}; +} + + +void pqxx::connection::check_overwritable() const +{ + if (m_trans) + throw pqxx::usage_error{ + "Moving a connection onto one with a transaction open."}; + if (not std::empty(m_errorhandlers)) + throw pqxx::usage_error{ + "Moving a connection onto one with error handlers registered."}; + if (not std::empty(m_receivers)) + throw usage_error{ + "Moving a connection onto one " + "with notification receivers registered."}; +} + + +pqxx::connection &pqxx::connection::operator=(connection &&rhs) +{ + check_overwritable(); + rhs.check_movable(); + + close(); + + m_conn = std::exchange(rhs.m_conn, nullptr); + m_unique_id = rhs.m_unique_id; + + return *this; +} + + +pqxx::result pqxx::connection::make_result( + internal::pq::PGresult *pgr, std::shared_ptr const &query, + std::string_view desc) +{ + if (pgr == nullptr) + { + if (is_open()) + throw failure(err_msg()); + else + throw broken_connection{"Lost connection to the database server."}; + } + internal::encoding_group enc; + try + { + enc = internal::enc_group(encoding_id()); + } + catch (std::exception const &) + { + // Don't let the PGresult leak. + // TODO: Can we just accept a unique_ptr instead? + internal::clear_result(pgr); + throw; + } + auto const r{pqxx::internal::gate::result_creation::create(pgr, query, enc)}; + pqxx::internal::gate::result_creation{r}.check_status(desc); + return r; +} + + +int PQXX_COLD pqxx::connection::backendpid() const &noexcept +{ + return (m_conn == nullptr) ? 0 : PQbackendPID(m_conn); +} + + +namespace +{ +PQXX_PURE int socket_of(::pqxx::internal::pq::PGconn const *c) noexcept +{ + return (c == nullptr) ? -1 : PQsocket(c); +} +} // namespace + + +int pqxx::connection::sock() const &noexcept +{ + return socket_of(m_conn); +} + + +int PQXX_COLD pqxx::connection::protocol_version() const noexcept +{ + return (m_conn == nullptr) ? 0 : PQprotocolVersion(m_conn); +} + + +int PQXX_COLD pqxx::connection::server_version() const noexcept +{ + return PQserverVersion(m_conn); +} + + +void pqxx::connection::set_variable( + std::string_view var, std::string_view value) & +{ + exec(internal::concat("SET ", quote_name(var), "=", value)); +} + + +std::string pqxx::connection::get_variable(std::string_view var) +{ + return exec(internal::concat("SHOW ", quote_name(var))) + .at(0) + .at(0) + .as(std::string{}); +} + + +std::string pqxx::connection::get_var(std::string_view var) +{ + // (Variables can't be null, so far as I can make out.) + return exec(internal::concat("SHOW "sv, quote_name(var)))[0][0] + .as(); +} + + +/** Set up various parts of logical connection state that may need to be + * recovered because the physical connection to the database was lost and is + * being reset, or that may not have been initialized yet. + */ +void pqxx::connection::set_up_state() +{ + if (auto const proto_ver{protocol_version()}; proto_ver < 3) + { + if (proto_ver == 0) + throw broken_connection{"No connection."}; + else + throw feature_not_supported{ + "Unsupported frontend/backend protocol version; 3.0 is the minimum."}; + } + + if (server_version() <= 90000) + throw feature_not_supported{ + "Unsupported server version; 9.0 is the minimum."}; + + // The default notice processor in libpq writes to stderr. Ours does + // nothing. + // If the caller registers an error handler, this gets replaced with an + // error handler that walks down the connection's chain of handlers. We + // don't do that by default because there's a danger: libpq may call the + // notice processor via a result object, even after the connection has been + // destroyed and the handlers list no longer exists. + PQXX_LIKELY + PQsetNoticeProcessor(m_conn, inert_notice_processor, nullptr); +} + + +bool pqxx::connection::is_open() const noexcept +{ + return status() == CONNECTION_OK; +} + + +void pqxx::connection::process_notice_raw(char const msg[]) noexcept +{ + if ((msg == nullptr) or (*msg == '\0')) + return; + auto const rbegin = std::crbegin(m_errorhandlers), + rend = std::crend(m_errorhandlers); + for (auto i{rbegin}; (i != rend) and (**i)(msg); ++i) + ; +} + + +void pqxx::connection::process_notice(char const msg[]) noexcept +{ + if (msg == nullptr) + return; + zview const view{msg}; + if (std::empty(view)) + return; + else if (msg[std::size(view) - 1] == '\n') + process_notice_raw(msg); + else + // Newline is missing. Let the zview version of the code add it. + PQXX_UNLIKELY + process_notice(view); +} + + +void pqxx::connection::process_notice(zview msg) noexcept +{ + if (std::empty(msg)) + return; + else if (msg[std::size(msg) - 1] == '\n') + process_notice_raw(msg.c_str()); + else + try + { + // Add newline. + std::string buf; + buf.reserve(std::size(msg) + 1); + buf.assign(msg); + buf.push_back('\n'); + process_notice_raw(buf.c_str()); + } + catch (std::exception const &) + { + // If nothing else works, try writing the message without the newline. + PQXX_UNLIKELY + process_notice_raw(msg.c_str()); + } +} + + +void PQXX_COLD pqxx::connection::trace(FILE *out) noexcept +{ + if (m_conn) + { + if (out) + PQtrace(m_conn, out); + else + PQuntrace(m_conn); + } +} + + +void PQXX_COLD pqxx::connection::add_receiver(pqxx::notification_receiver *n) +{ + if (n == nullptr) + throw argument_error{"Null receiver registered"}; + + // Add to receiver list and attempt to start listening. + auto const p{m_receivers.find(n->channel())}; + auto const new_value{receiver_list::value_type{n->channel(), n}}; + + if (p == std::end(m_receivers)) + { + // Not listening on this event yet, start doing so. + auto const lq{std::make_shared( + internal::concat("LISTEN ", quote_name(n->channel())))}; + make_result(PQexec(m_conn, lq->c_str()), lq, *lq); + m_receivers.insert(new_value); + } + else + { + m_receivers.insert(p, new_value); + } +} + + +void PQXX_COLD +pqxx::connection::remove_receiver(pqxx::notification_receiver *T) noexcept +{ + if (T == nullptr) + return; + + try + { + auto needle{ + std::pair{T->channel(), T}}; + auto R{m_receivers.equal_range(needle.first)}; + auto i{find(R.first, R.second, needle)}; + + if (i == R.second) + { + PQXX_UNLIKELY + process_notice(internal::concat( + "Attempt to remove unknown receiver '", needle.first, "'")); + } + else + { + // Erase first; otherwise a notification for the same receiver may yet + // come in and wreak havoc. Thanks Dragan Milenkovic. + bool const gone{R.second == ++R.first}; + m_receivers.erase(i); + if (gone) + exec(internal::concat("UNLISTEN ", quote_name(needle.first)).c_str()); + } + } + catch (std::exception const &e) + { + PQXX_UNLIKELY + process_notice(e.what()); + } +} + + +bool pqxx::connection::consume_input() noexcept +{ + return PQconsumeInput(m_conn) != 0; +} + + +bool pqxx::connection::is_busy() const noexcept +{ + return PQisBusy(m_conn) != 0; +} + + +void PQXX_COLD pqxx::connection::cancel_query() +{ + using pointer = std::unique_ptr>; + pointer cancel{PQgetCancel(m_conn), PQfreeCancel}; + if (cancel == nullptr) + PQXX_UNLIKELY + throw std::bad_alloc{}; + + std::array errbuf; + auto const err{errbuf.data()}; + auto const c{ + PQcancel(cancel.get(), err, static_cast(std::size(errbuf)))}; + if (c == 0) + PQXX_UNLIKELY + throw pqxx::sql_error{std::string{err, std::size(errbuf)}, "[cancel]"}; +} + + +namespace +{ +// C++20: std::span? +/// Get error string for a given @c errno value. +template +char const *PQXX_COLD +error_string(int err_num, std::array &buffer) +{ + // Not entirely clear whether strerror_s will be in std or global namespace. + using namespace std; + +#if defined(PQXX_HAVE_STERROR_S) || defined(PQXX_HAVE_STRERROR_R) +# if defined(PQXX_HAVE_STRERROR_S) + auto const err_result{strerror_s(std::data(buffer), BYTES, err_num)}; +# else + auto const err_result{strerror_r(err_num, std::data(buffer), BYTES)}; +# endif + if constexpr (std::is_same_v, char *>) + { + // GNU version of strerror_r; returns the error string, which may or may + // not reside within buffer. + return err_result; + } + else + { + // Either strerror_s or POSIX strerror_r; returns an error code. + // Sorry for being lazy here: Not reporting error string for the case + // where we can't retrieve an error string. + if (err_result == 0) + return std::data(buffer); + else + return "Compound errors."; + } + +#else + // Fallback case, hopefully for no actual platforms out there. + pqxx::ignore_unused(err_num, buffer); + return "(No error information available.)"; +#endif +} +} // namespace + + +#if defined(_WIN32) || __has_include() +void pqxx::connection::set_blocking(bool block) & +{ + auto const fd{sock()}; +# if defined _WIN32 + unsigned long mode{not block}; + if (::ioctlsocket(fd, FIONBIO, &mode) != 0) + { + std::array errbuf; + char const *err{error_string(WSAGetLastError(), errbuf)}; + throw broken_connection{ + internal::concat("Could not set socket's blocking mode: ", err)}; + } +# else // _WIN32 + std::array errbuf; + auto flags{::fcntl(fd, F_GETFL, 0)}; + if (flags == -1) + { + char const *const err{error_string(errno, errbuf)}; + throw broken_connection{ + internal::concat("Could not get socket state: ", err)}; + } + if (block) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + if (::fcntl(fd, F_SETFL, flags) == -1) + { + char const *const err{error_string(errno, errbuf)}; + throw broken_connection{ + internal::concat("Could not set socket's blocking mode: ", err)}; + } +# endif // _WIN32 +} +#endif // defined(_WIN32) || __has_include() + + +void PQXX_COLD +pqxx::connection::set_verbosity(error_verbosity verbosity) &noexcept +{ + PQsetErrorVerbosity(m_conn, static_cast(verbosity)); +} + + +namespace +{ +/// Unique pointer to PGnotify. +using notify_ptr = std::unique_ptr>; + + +/// Get one notification from a connection, or null. +notify_ptr get_notif(pqxx::internal::pq::PGconn *conn) +{ + return notify_ptr(PQnotifies(conn), PQfreemem); +} +} // namespace + + +int pqxx::connection::get_notifs() +{ + if (not consume_input()) + throw broken_connection{"Connection lost."}; + + // Even if somehow we receive notifications during our transaction, don't + // deliver them. + if (m_trans) + PQXX_UNLIKELY + return 0; + + int notifs = 0; + for (auto N{get_notif(m_conn)}; N.get(); N = get_notif(m_conn)) + { + notifs++; + + auto const Hit{m_receivers.equal_range(std::string{N->relname})}; + if (Hit.second != Hit.first) + { + std::string payload{N->extra}; + for (auto i{Hit.first}; i != Hit.second; ++i) try + { + (*i->second)(payload, N->be_pid); + } + catch (std::exception const &e) + { + try + { + process_notice(internal::concat( + "Exception in notification receiver '", i->first, + "': ", e.what(), "\n")); + } + catch (std::bad_alloc const &) + { + // Out of memory. Try to get the message out in a more robust way. + process_notice( + "Exception in notification receiver, " + "and also ran out of memory\n"); + } + catch (std::exception const &) + { + process_notice( + "Exception in notification receiver " + "(compounded by other error)\n"); + } + } + } + + N.reset(); + } + return notifs; +} + + +char const *PQXX_COLD pqxx::connection::dbname() const +{ + return PQdb(m_conn); +} + + +char const *PQXX_COLD pqxx::connection::username() const +{ + return PQuser(m_conn); +} + + +char const *PQXX_COLD pqxx::connection::hostname() const +{ + return PQhost(m_conn); +} + + +char const *PQXX_COLD pqxx::connection::port() const +{ + return PQport(m_conn); +} + + +char const *pqxx::connection::err_msg() const noexcept +{ + return (m_conn == nullptr) ? "No connection to database" : + PQerrorMessage(m_conn); +} + + +void PQXX_COLD pqxx::connection::register_errorhandler(errorhandler *handler) +{ + // Set notice processor on demand, i.e. only when the caller actually + // registers an error handler. + // We do this just to make it less likely that users fall into the trap + // where a result object may hold a notice processor derived from its parent + // connection which has already been destroyed. Our notice processor goes + // through the connection's list of error handlers. If the connection object + // has already been destroyed though, that list no longer exists. + // By setting the notice processor on demand, we absolve users who never + // register an error handler from ahving to care about this nasty subtlety. + if (std::empty(m_errorhandlers)) + PQsetNoticeProcessor(m_conn, pqxx_notice_processor, this); + m_errorhandlers.push_back(handler); +} + + +void PQXX_COLD +pqxx::connection::unregister_errorhandler(errorhandler *handler) noexcept +{ + // The errorhandler itself will take care of nulling its pointer to this + // connection. + m_errorhandlers.remove(handler); + if (std::empty(m_errorhandlers)) + PQsetNoticeProcessor(m_conn, inert_notice_processor, nullptr); +} + + +std::vector + PQXX_COLD pqxx::connection::get_errorhandlers() const +{ + return {std::begin(m_errorhandlers), std::end(m_errorhandlers)}; +} + + +pqxx::result +pqxx::connection::exec(std::string_view query, std::string_view desc) +{ + return exec(std::make_shared(query), desc); +} + + +pqxx::result pqxx::connection::exec( + std::shared_ptr query, std::string_view desc) +{ + auto const res{make_result(PQexec(m_conn, query->c_str()), query, desc)}; + get_notifs(); + return res; +} + + +std::string pqxx::connection::encrypt_password( + char const user[], char const password[], char const *algorithm) +{ +#if defined(PQXX_HAVE_PQENCRYPTPASSWORDCONN) + { + auto const buf{PQencryptPasswordConn(m_conn, password, user, algorithm)}; + std::unique_ptr> ptr{ + buf, [](char const *x) { PQfreemem(const_cast(x)); }}; + return std::string(ptr.get()); + } +#else + { + // No PQencryptPasswordConn. Fall back on the old PQencryptPassword... + // unless the caller selects a different algorithm. + if (algorithm != nullptr and std::strcmp(algorithm, "md5") != 0) + throw feature_not_supported{ + "Could not encrypt password: available libpq version does not support " + "algorithms other than md5."}; +# include "pqxx/internal/ignore-deprecated-pre.hxx" + return pqxx::encrypt_password(user, password); +# include "pqxx/internal/ignore-deprecated-post.hxx" + } +#endif // PQXX_HAVE_PQENCRYPTPASSWORDCONN +} + + +void pqxx::connection::prepare(char const name[], char const definition[]) & +{ + auto const q{std::make_shared( + pqxx::internal::concat("[PREPARE ", name, "]"))}; + + auto const r{ + make_result(PQprepare(m_conn, name, definition, 0, nullptr), q, *q)}; +} + + +void pqxx::connection::prepare(char const definition[]) & +{ + this->prepare("", definition); +} + + +void pqxx::connection::unprepare(std::string_view name) +{ + exec(internal::concat("DEALLOCATE ", quote_name(name))); +} + + +pqxx::result pqxx::connection::exec_prepared( + std::string_view statement, internal::c_params const &args) +{ + auto const q{std::make_shared(statement)}; + auto const pq_result{PQexecPrepared( + m_conn, q->c_str(), + check_cast(std::size(args.values), "exec_prepared"sv), + args.values.data(), args.lengths.data(), + reinterpret_cast(args.formats.data()), + static_cast(format::text))}; + auto const r{make_result(pq_result, q, statement)}; + get_notifs(); + return r; +} + + +void pqxx::connection::close() +{ + try + { + if (m_trans) + PQXX_UNLIKELY + process_notice(internal::concat( + "Closing connection while ", + internal::describe_object("transaction"sv, m_trans->name()), + " is still open.")); + + if (not std::empty(m_receivers)) + { + PQXX_UNLIKELY + process_notice("Closing connection with outstanding receivers."); + m_receivers.clear(); + } + + std::list old_handlers; + m_errorhandlers.swap(old_handlers); + auto const rbegin{std::crbegin(old_handlers)}, + rend{std::crend(old_handlers)}; + for (auto i{rbegin}; i != rend; ++i) + pqxx::internal::gate::errorhandler_connection{**i}.unregister(); + + PQfinish(m_conn); + m_conn = nullptr; + } + catch (std::exception const &) + { + m_conn = nullptr; + throw; + } +} + + +int pqxx::connection::status() const noexcept +{ + return PQstatus(m_conn); +} + + +namespace +{ +/// Return a name for t, if t is non-null and has a name; or empty string. +std::string_view get_name(pqxx::transaction_base const *t) +{ + return (t == nullptr) ? ""sv : t->name(); +} +} // namespace + + +void pqxx::connection::register_transaction(transaction_base *t) +{ + internal::check_unique_register( + m_trans, "transaction", get_name(m_trans), t, "transaction", get_name(t)); + m_trans = t; +} + + +void pqxx::connection::unregister_transaction(transaction_base *t) noexcept +{ + try + { + internal::check_unique_unregister( + m_trans, "transaction", get_name(m_trans), t, "transaction", + get_name(t)); + } + catch (std::exception const &e) + { + process_notice(e.what()); + } + m_trans = nullptr; +} + + +std::pair>, std::size_t> +pqxx::connection::read_copy_line() +{ + char *buf{nullptr}; + + // Allocate once, re-use across invocations. + static auto const q{std::make_shared("[END COPY]")}; + + auto const line_len{PQgetCopyData(m_conn, &buf, false)}; + switch (line_len) + { + case -2: // Error. + throw failure{ + internal::concat("Reading of table data failed: ", err_msg())}; + + case -1: // End of COPY. + make_result(PQgetResult(m_conn), q, *q); + return {}; + + case 0: // "Come back later." + throw internal_error{"table read inexplicably went asynchronous"}; + + default: // Success, got buffer size. + // Line size includes a trailing zero, which we ignore. + auto const text_len{static_cast(line_len) - 1}; + return std::make_pair( + std::unique_ptr>{buf, PQfreemem}, + text_len); + } +} + + +void pqxx::connection::write_copy_line(std::string_view line) +{ + static std::string const err_prefix{"Error writing to table: "}; + auto const size{check_cast( + internal::ssize(line), "Line in stream_to is too long to process."sv)}; + if (PQputCopyData(m_conn, line.data(), size) <= 0) + PQXX_UNLIKELY + throw failure{err_prefix + err_msg()}; + if (PQputCopyData(m_conn, "\n", 1) <= 0) + PQXX_UNLIKELY + throw failure{err_prefix + err_msg()}; +} + + +void pqxx::connection::end_copy_write() +{ + int res{PQputCopyEnd(m_conn, nullptr)}; + switch (res) + { + case -1: + throw failure{internal::concat("Write to table failed: ", err_msg())}; + case 0: throw internal_error{"table write is inexplicably asynchronous"}; + case 1: + // Normal termination. Retrieve result object. + break; + + default: + throw internal_error{ + internal::concat("unexpected result ", res, " from PQputCopyEnd()")}; + } + + static auto const q{std::make_shared("[END COPY]")}; + make_result(PQgetResult(m_conn), q, *q); +} + + +void pqxx::connection::start_exec(char const query[]) +{ + if (PQsendQuery(m_conn, query) == 0) + PQXX_UNLIKELY + throw failure{err_msg()}; +} + + +pqxx::internal::pq::PGresult *pqxx::connection::get_result() +{ + return PQgetResult(m_conn); +} + + +size_t pqxx::connection::esc_to_buf(std::string_view text, char *buf) const +{ + int err{0}; + auto const copied{ + PQescapeStringConn(m_conn, buf, text.data(), std::size(text), &err)}; + if (err) + PQXX_UNLIKELY + throw argument_error{err_msg()}; + return copied; +} + + +std::string pqxx::connection::esc(std::string_view text) const +{ + std::string buf; + buf.resize(2 * std::size(text) + 1); + auto const copied{esc_to_buf(text, buf.data())}; + buf.resize(copied); + return buf; +} + + +std::string PQXX_COLD +pqxx::connection::esc_raw(unsigned char const bin[], std::size_t len) const +{ + return pqxx::internal::esc_bin(binary_cast(bin, len)); +} + + +std::string +pqxx::connection::esc_raw(std::basic_string_view bin) const +{ + return pqxx::internal::esc_bin(bin); +} + + +std::string PQXX_COLD pqxx::connection::unesc_raw(char const text[]) const +{ + if (text[0] == '\\' and text[1] == 'x') + { + // Hex-escaped format. + std::string buf; + buf.resize(pqxx::internal::size_unesc_bin(std::strlen(text))); + pqxx::internal::unesc_bin( + std::string_view{text}, reinterpret_cast(buf.data())); + return buf; + } + else + { + // Legacy escape format. + // TODO: Remove legacy support. + std::size_t len; + auto bytes{const_cast( + reinterpret_cast(text))}; + std::unique_ptr> const + ptr{PQunescapeBytea(bytes, &len), PQfreemem}; + return std::string{ptr.get(), ptr.get() + len}; + } +} + + +std::string PQXX_COLD +pqxx::connection::quote_raw(unsigned char const bin[], std::size_t len) const +{ + return internal::concat("'", esc_raw(binary_cast(bin, len)), "'::bytea"); +} + + +std::string +pqxx::connection::quote_raw(std::basic_string_view bytes) const +{ + return internal::concat("'", esc_raw(bytes), "'::bytea"); +} + + +std::string PQXX_COLD pqxx::connection::quote(binarystring const &b) const +{ + return quote(b.bytes_view()); +} + + +std::string pqxx::connection::quote(std::basic_string_view b) const +{ + return internal::concat("'", esc_raw(b), "'::bytea"); +} + + +std::string pqxx::connection::quote_name(std::string_view identifier) const +{ + std::unique_ptr> buf{ + PQescapeIdentifier(m_conn, identifier.data(), std::size(identifier)), + PQfreemem}; + if (buf.get() == nullptr) + PQXX_UNLIKELY + throw failure{err_msg()}; + return std::string{buf.get()}; +} + + +std::string pqxx::connection::quote_table(std::string_view table_name) const +{ + return this->quote_name(table_name); +} + + +std::string pqxx::connection::quote_table(table_path path) const +{ + return separated_list( + ".", std::begin(path), std::end(path), + [this](auto name) { return this->quote_name(*name); }); +} + + +std::string +pqxx::connection::esc_like(std::string_view text, char escape_char) const +{ + std::string out; + out.reserve(std::size(text)); + internal::for_glyphs( + internal::enc_group(encoding_id()), + [&out, escape_char](char const *gbegin, char const *gend) { + if ((gend - gbegin == 1) and (*gbegin == '_' or *gbegin == '%')) + // We're not expecting a lot of wildcards in a string. Usually. + PQXX_UNLIKELY + out.push_back(escape_char); + + for (; gbegin != gend; ++gbegin) out.push_back(*gbegin); + }, + text.data(), std::size(text)); + return out; +} + + +int pqxx::connection::await_notification() +{ + int notifs = get_notifs(); + if (notifs == 0) + { + PQXX_LIKELY + internal::wait_fd(socket_of(m_conn), true, false, 10, 0); + notifs = get_notifs(); + } + return notifs; +} + + +int pqxx::connection::await_notification( + std::time_t seconds, long microseconds) +{ + int notifs = get_notifs(); + if (notifs == 0) + { + PQXX_LIKELY + internal::wait_fd( + socket_of(m_conn), true, false, + check_cast(seconds, "Seconds out of range."), + check_cast(microseconds, "Microseconds out of range.")); + return get_notifs(); + } + return notifs; +} + + +std::string pqxx::connection::adorn_name(std::string_view n) +{ + auto const id{to_string(++m_unique_id)}; + if (std::empty(n)) + return pqxx::internal::concat("x", id); + else + return pqxx::internal::concat(n, "_", id); +} + + +std::string pqxx::connection::get_client_encoding() const +{ + return internal::name_encoding(encoding_id()); +} + + +void PQXX_COLD pqxx::connection::set_client_encoding(char const encoding[]) & +{ + switch (auto const retval{PQsetClientEncoding(m_conn, encoding)}; retval) + { + case 0: + // OK. + PQXX_LIKELY + break; + case -1: + PQXX_UNLIKELY + if (is_open()) + throw failure{"Setting client encoding failed."}; + else + throw broken_connection{"Lost connection to the database server."}; + default: + PQXX_UNLIKELY + throw internal_error{internal::concat( + "Unexpected result from PQsetClientEncoding: ", retval)}; + } +} + + +int pqxx::connection::encoding_id() const +{ + int const enc{PQclientEncoding(m_conn)}; + if (enc == -1) + { + // PQclientEncoding does not query the database, but it does check for + // broken connections. And unfortunately, we check the encoding right + // *before* checking a query result for failure. So, we need to handle + // connection failure here and it will apply in lots of places. + // TODO: Make pqxx::result::result(...) do all the checking. + PQXX_UNLIKELY + if (is_open()) + throw failure{"Could not obtain client encoding."}; + else + throw broken_connection{"Lost connection to the database server."}; + } + PQXX_LIKELY + return enc; +} + + +pqxx::result pqxx::connection::exec_params( + std::string_view query, internal::c_params const &args) +{ + auto const q{std::make_shared(query)}; + auto const pq_result{PQexecParams( + m_conn, q->c_str(), + check_cast(std::size(args.values), "exec_params"sv), nullptr, + args.values.data(), args.lengths.data(), + reinterpret_cast(args.formats.data()), + static_cast(format::text))}; + auto const r{make_result(pq_result, q)}; + get_notifs(); + return r; +} + + +namespace +{ +/// Get the prevailing default value for a connection parameter. +char const *get_default(PQconninfoOption const &opt) noexcept +{ + if (opt.envvar == nullptr) + { + // There's no environment variable for this setting. The only default is + // the one that was compiled in. + return opt.compiled; + } + // As of C++11, std::getenv() uses thread-local storage, so it should be + // thread-safe. MSVC still warns about it though. +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4996) +#endif + char const *var{std::getenv(opt.envvar)}; +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + if (var == nullptr) + { + // There's an environment variable for this setting, but it's not set. + return opt.compiled; + } + + // The environment variable is the prevailing default. + return var; +} +} // namespace + + +std::string pqxx::connection::connection_string() const +{ + if (m_conn == nullptr) + PQXX_UNLIKELY + throw usage_error{"Can't get connection string: connection is not open."}; + + std::unique_ptr< + PQconninfoOption, std::function> const params{ + PQconninfo(m_conn), PQconninfoFree}; + if (params.get() == nullptr) + PQXX_UNLIKELY + throw std::bad_alloc{}; + + std::string buf; + for (std::size_t i{0}; params.get()[i].keyword != nullptr; ++i) + { + auto const param{params.get()[i]}; + if (param.val != nullptr) + { + auto const default_val{get_default(param)}; + if ( + (default_val == nullptr) or (std::strcmp(param.val, default_val) != 0)) + { + if (not std::empty(buf)) + buf.push_back(' '); + buf += param.keyword; + buf.push_back('='); + buf += param.val; + } + } + } + return buf; +} + + +#if defined(_WIN32) || __has_include() +pqxx::connecting::connecting(zview connection_string) : + m_conn{connection::connect_nonblocking, connection_string} +{} +#endif // defined(_WIN32) || __has_include( + + +#if defined(_WIN32) || __has_include() +void pqxx::connecting::process() & +{ + auto const [reading, writing]{m_conn.poll_connect()}; + m_reading = reading; + m_writing = writing; +} +#endif // defined(_WIN32) || __has_include( + + +#if defined(_WIN32) || __has_include() +pqxx::connection pqxx::connecting::produce() && +{ + if (!done()) + throw usage_error{ + "Tried to produce a nonblocking connection before it was done."}; + m_conn.complete_init(); + return std::move(m_conn); +} +#endif // defined(_WIN32) || __has_include( diff --git a/ext/libpqxx-7.7.3/src/cursor.cxx b/ext/libpqxx-7.7.3/src/cursor.cxx new file mode 100644 index 000000000..28e8ba42f --- /dev/null +++ b/ext/libpqxx-7.7.3/src/cursor.cxx @@ -0,0 +1,339 @@ +/** Implementation of libpqxx STL-style cursor classes. + * + * These classes wrap SQL cursors in STL-like interfaces. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/cursor.hxx" +#include "pqxx/internal/gates/icursor_iterator-icursorstream.hxx" +#include "pqxx/internal/gates/icursorstream-icursor_iterator.hxx" +#include "pqxx/result.hxx" +#include "pqxx/strconv.hxx" +#include "pqxx/transaction.hxx" + +#include "pqxx/internal/header-post.hxx" + + +pqxx::cursor_base::difference_type pqxx::cursor_base::all() noexcept +{ + // Implemented out-of-line so we don't fall afoul of Visual Studio defining + // min() and max() macros, which turn this expression into malformed code: + return std::numeric_limits::max() - 1; +} + + +pqxx::cursor_base::difference_type pqxx::cursor_base::backward_all() noexcept +{ + // Implemented out-of-line so we don't fall afoul of Visual Studio defining + // min() and max() macros, which turn this expression into malformed code: + return std::numeric_limits::min() + 1; +} + + +pqxx::cursor_base::cursor_base( + connection &context, std::string_view Name, bool embellish_name) : + m_name{embellish_name ? context.adorn_name(Name) : Name} +{} + + +pqxx::result::size_type +pqxx::internal::obtain_stateless_cursor_size(sql_cursor &cur) +{ + if (cur.endpos() == -1) + cur.move(cursor_base::all()); + return result::size_type(cur.endpos() - 1); +} + + +pqxx::result pqxx::internal::stateless_cursor_retrieve( + sql_cursor &cur, result::difference_type size, + result::difference_type begin_pos, result::difference_type end_pos) +{ + if (begin_pos < 0 or begin_pos > size) + throw range_error{"Starting position out of range"}; + + if (end_pos < -1) + end_pos = -1; + else if (end_pos > size) + end_pos = size; + + if (begin_pos == end_pos) + return cur.empty_result(); + + int const direction{((begin_pos < end_pos) ? 1 : -1)}; + cur.move((begin_pos - direction) - (cur.pos() - 1)); + return cur.fetch(end_pos - begin_pos); +} + + +pqxx::icursorstream::icursorstream( + transaction_base &context, std::string_view query, std::string_view basename, + difference_type sstride) : + m_cur{ + context, + query, + basename, + cursor_base::forward_only, + cursor_base::read_only, + cursor_base::owned, + false}, + m_stride{sstride}, + m_realpos{0}, + m_reqpos{0}, + m_iterators{nullptr}, + m_done{false} +{ + set_stride(sstride); +} + + +pqxx::icursorstream::icursorstream( + transaction_base &context, field const &cname, difference_type sstride, + cursor_base::ownership_policy op) : + m_cur{context, cname.c_str(), op}, + m_stride{sstride}, + m_realpos{0}, + m_reqpos{0}, + m_iterators{nullptr}, + m_done{false} +{ + set_stride(sstride); +} + + +void pqxx::icursorstream::set_stride(difference_type stride) & +{ + if (stride < 1) + throw argument_error{ + internal::concat("Attempt to set cursor stride to ", stride)}; + m_stride = stride; +} + + +pqxx::result pqxx::icursorstream::fetchblock() +{ + result const r{m_cur.fetch(m_stride)}; + m_realpos += std::size(r); + if (std::empty(r)) + m_done = true; + return r; +} + + +pqxx::icursorstream &pqxx::icursorstream::ignore(std::streamsize n) & +{ + auto offset{m_cur.move(difference_type(n))}; + m_realpos += offset; + if (offset < n) + m_done = true; + return *this; +} + + +pqxx::icursorstream::size_type pqxx::icursorstream::forward(size_type n) +{ + m_reqpos += difference_type(n) * m_stride; + return icursorstream::size_type(m_reqpos); +} + + +void pqxx::icursorstream::insert_iterator(icursor_iterator *i) noexcept +{ + pqxx::internal::gate::icursor_iterator_icursorstream{*i}.set_next( + m_iterators); + if (m_iterators != nullptr) + pqxx::internal::gate::icursor_iterator_icursorstream{*m_iterators} + .set_prev(i); + m_iterators = i; +} + + +void pqxx::icursorstream::remove_iterator(icursor_iterator *i) const noexcept +{ + pqxx::internal::gate::icursor_iterator_icursorstream igate{*i}; + if (i == m_iterators) + { + m_iterators = igate.get_next(); + if (m_iterators != nullptr) + pqxx::internal::gate::icursor_iterator_icursorstream{*m_iterators} + .set_prev(nullptr); + } + else + { + auto prev{igate.get_prev()}, next{igate.get_next()}; + pqxx::internal::gate::icursor_iterator_icursorstream{*prev}.set_next(next); + if (next != nullptr) + pqxx::internal::gate::icursor_iterator_icursorstream{*next}.set_prev( + prev); + } + igate.set_prev(nullptr); + igate.set_next(nullptr); +} + + +void pqxx::icursorstream::service_iterators(difference_type topos) +{ + if (topos < m_realpos) + return; + + using todolist = std::multimap; + todolist todo; + for (icursor_iterator *i{m_iterators}, *next; i != nullptr; i = next) + { + pqxx::internal::gate::icursor_iterator_icursorstream gate{*i}; + auto const ipos{gate.pos()}; + if (ipos >= m_realpos and ipos <= topos) + todo.insert(todolist::value_type(ipos, i)); + next = gate.get_next(); + } + auto const todo_end = std::end(todo); + for (auto i{std::begin(todo)}; i != todo_end;) + { + auto const readpos{i->first}; + if (readpos > m_realpos) + ignore(readpos - m_realpos); + result const r{fetchblock()}; + for (; i != todo_end and i->first == readpos; ++i) + pqxx::internal::gate::icursor_iterator_icursorstream{*i->second}.fill(r); + } +} + + +pqxx::icursor_iterator::icursor_iterator() noexcept : m_pos{0} {} + + +pqxx::icursor_iterator::icursor_iterator(istream_type &s) noexcept : + m_stream{&s}, + m_pos{difference_type( + pqxx::internal::gate::icursorstream_icursor_iterator(s).forward(0))} +{ + pqxx::internal::gate::icursorstream_icursor_iterator{*m_stream} + .insert_iterator(this); +} + + +pqxx::icursor_iterator::icursor_iterator(icursor_iterator const &rhs) noexcept + : + m_stream{rhs.m_stream}, m_here{rhs.m_here}, m_pos{rhs.m_pos} +{ + if (m_stream != nullptr) + pqxx::internal::gate::icursorstream_icursor_iterator{*m_stream} + .insert_iterator(this); +} + + +pqxx::icursor_iterator::~icursor_iterator() noexcept +{ + if (m_stream != nullptr) + pqxx::internal::gate::icursorstream_icursor_iterator{*m_stream} + .remove_iterator(this); +} + + +pqxx::icursor_iterator pqxx::icursor_iterator::operator++(int) +{ + icursor_iterator old{*this}; + m_pos = difference_type( + pqxx::internal::gate::icursorstream_icursor_iterator{*m_stream}.forward()); + m_here.clear(); + return old; +} + + +pqxx::icursor_iterator &pqxx::icursor_iterator::operator++() +{ + m_pos = difference_type( + pqxx::internal::gate::icursorstream_icursor_iterator{*m_stream}.forward()); + m_here.clear(); + return *this; +} + + +pqxx::icursor_iterator &pqxx::icursor_iterator::operator+=(difference_type n) +{ + if (n <= 0) + { + PQXX_UNLIKELY + if (n == 0) + return *this; + throw argument_error{"Advancing icursor_iterator by negative offset."}; + } + PQXX_LIKELY + m_pos = difference_type( + pqxx::internal::gate::icursorstream_icursor_iterator{*m_stream}.forward( + icursorstream::size_type(n))); + m_here.clear(); + return *this; +} + + +pqxx::icursor_iterator & +pqxx::icursor_iterator::operator=(icursor_iterator const &rhs) noexcept +{ + if (rhs.m_stream == m_stream) + { + PQXX_UNLIKELY + m_here = rhs.m_here; + m_pos = rhs.m_pos; + } + else + { + PQXX_LIKELY + if (m_stream != nullptr) + pqxx::internal::gate::icursorstream_icursor_iterator{*m_stream} + .remove_iterator(this); + m_here = rhs.m_here; + m_pos = rhs.m_pos; + m_stream = rhs.m_stream; + if (m_stream != nullptr) + pqxx::internal::gate::icursorstream_icursor_iterator{*m_stream} + .insert_iterator(this); + } + return *this; +} + + +bool pqxx::icursor_iterator::operator==(icursor_iterator const &rhs) const +{ + if (m_stream == rhs.m_stream) + return pos() == rhs.pos(); + if (m_stream != nullptr and rhs.m_stream != nullptr) + return false; + refresh(); + rhs.refresh(); + return std::empty(m_here) and std::empty(rhs.m_here); +} + + +bool pqxx::icursor_iterator::operator<(icursor_iterator const &rhs) const +{ + if (m_stream == rhs.m_stream) + return pos() < rhs.pos(); + refresh(); + rhs.refresh(); + return not std::empty(m_here); +} + + +void pqxx::icursor_iterator::refresh() const +{ + if (m_stream != nullptr) + pqxx::internal::gate::icursorstream_icursor_iterator{*m_stream} + .service_iterators(pos()); +} + + +void pqxx::icursor_iterator::fill(result const &r) +{ + m_here = r; +} diff --git a/ext/libpqxx-7.7.3/src/encodings.cxx b/ext/libpqxx-7.7.3/src/encodings.cxx new file mode 100644 index 000000000..99f038724 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/encodings.cxx @@ -0,0 +1,839 @@ +/** Implementation of string encodings support + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include +#include +#include + +extern "C" +{ +#include +} + + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/except.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/encodings.hxx" +#include "pqxx/strconv.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace pqxx +{ +PQXX_DECLARE_ENUM_CONVERSION(pqxx::internal::encoding_group); +} + + +// Internal helper functions +namespace +{ +/// Extract byte from buffer, return as unsigned char. +constexpr PQXX_PURE unsigned char +get_byte(char const buffer[], std::size_t offset) noexcept +{ + return static_cast(buffer[offset]); +} + + +[[noreturn]] void throw_for_encoding_error( + char const *encoding_name, char const buffer[], std::size_t start, + std::size_t count) +{ + std::stringstream s; + s << "Invalid byte sequence for encoding " << encoding_name << " at byte " + << start << ": " << std::hex << std::setw(2) << std::setfill('0'); + for (std::size_t i{0}; i < count; ++i) + { + s << "0x" << static_cast(get_byte(buffer, start + i)); + if (i + 1 < count) + s << " "; + } + throw pqxx::argument_error{s.str()}; +} + + +/// Does value lie between bottom and top, inclusive? +constexpr PQXX_PURE bool +between_inc(unsigned char value, unsigned bottom, unsigned top) +{ + return value >= bottom and value <= top; +} + + +/* +EUC-JP and EUC-JIS-2004 represent slightly different code points but iterate +the same: + * https://en.wikipedia.org/wiki/Extended_Unix_Code#EUC-JP + * http://x0213.org/codetable/index.en.html +*/ +PQXX_PURE std::size_t next_seq_for_euc_jplike( + char const buffer[], std::size_t buffer_len, std::size_t start, + char const encoding_name[]) +{ + if (start >= buffer_len) + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80) + return start + 1; + + if (start + 2 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error(encoding_name, buffer, start, 1); + + auto const byte2{get_byte(buffer, start + 1)}; + if (byte1 == 0x8e) + { + if (not between_inc(byte2, 0xa1, 0xfe)) + PQXX_UNLIKELY + throw_for_encoding_error(encoding_name, buffer, start, 2); + + return start + 2; + } + + if (between_inc(byte1, 0xa1, 0xfe)) + { + if (not between_inc(byte2, 0xa1, 0xfe)) + PQXX_UNLIKELY + throw_for_encoding_error(encoding_name, buffer, start, 2); + + return start + 2; + } + + if (byte1 == 0x8f and start + 3 <= buffer_len) + { + auto const byte3{get_byte(buffer, start + 2)}; + if ( + not between_inc(byte2, 0xa1, 0xfe) or not between_inc(byte3, 0xa1, 0xfe)) + PQXX_UNLIKELY + throw_for_encoding_error(encoding_name, buffer, start, 3); + + return start + 3; + } + + throw_for_encoding_error(encoding_name, buffer, start, 1); +} + +/* +As far as I can tell, for the purposes of iterating the only difference between +SJIS and SJIS-2004 is increased range in the first byte of two-byte sequences +(0xEF increased to 0xFC). Officially, that is; apparently the version of SJIS +used by Postgres has the same range as SJIS-2004. They both have increased +range over the documented versions, not having the even/odd restriction for the +first byte in 2-byte sequences. +*/ +// https://en.wikipedia.org/wiki/Shift_JIS#Shift_JIS_byte_map +// http://x0213.org/codetable/index.en.html +PQXX_PURE std::size_t next_seq_for_sjislike( + char const buffer[], std::size_t buffer_len, std::size_t start, + char const *encoding_name) +{ + if (start >= buffer_len) + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80 or between_inc(byte1, 0xa1, 0xdf)) + return start + 1; + + if ( + not between_inc(byte1, 0x81, 0x9f) and not between_inc(byte1, 0xe0, 0xfc)) + PQXX_UNLIKELY + throw_for_encoding_error(encoding_name, buffer, start, 1); + + if (start + 2 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error(encoding_name, buffer, start, buffer_len - start); + + auto const byte2{get_byte(buffer, start + 1)}; + if (byte2 == 0x7f) + PQXX_UNLIKELY + throw_for_encoding_error(encoding_name, buffer, start, 2); + + if (between_inc(byte2, 0x40, 0x9e) or between_inc(byte2, 0x9f, 0xfc)) + return start + 2; + + PQXX_UNLIKELY + throw_for_encoding_error(encoding_name, buffer, start, 2); +} +} // namespace + + +// Implement template specializations first. +namespace pqxx::internal +{ +template struct glyph_scanner +{ + PQXX_PURE static std::size_t + call(char const buffer[], std::size_t buffer_len, std::size_t start); +}; + +template<> struct glyph_scanner +{ + static PQXX_PURE constexpr std::size_t + call(char const /* buffer */[], std::size_t buffer_len, std::size_t start) + { + if (start >= buffer_len) + return std::string::npos; + else + return start + 1; + } +}; + +// https://en.wikipedia.org/wiki/Big5#Organization +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + if (start >= buffer_len) + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80) + return start + 1; + + if (not between_inc(byte1, 0x81, 0xfe) or (start + 2 > buffer_len)) + PQXX_UNLIKELY + throw_for_encoding_error("BIG5", buffer, start, 1); + + auto const byte2{get_byte(buffer, start + 1)}; + if ( + not between_inc(byte2, 0x40, 0x7e) and not between_inc(byte2, 0xa1, 0xfe)) + PQXX_UNLIKELY + throw_for_encoding_error("BIG5", buffer, start, 2); + + return start + 2; +} + +/* +The PostgreSQL documentation claims that the EUC_* encodings are 1-3 bytes +each, but other documents explain that the EUC sets can contain 1-(2,3,4) bytes +depending on the specific extension: + EUC_CN : 1-2 + EUC_JP : 1-3 + EUC_JIS_2004: 1-2 + EUC_KR : 1-2 + EUC_TW : 1-4 +*/ + +// https://en.wikipedia.org/wiki/GB_2312#EUC-CN +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + if (start >= buffer_len) + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80) + return start + 1; + + if (not between_inc(byte1, 0xa1, 0xf7) or start + 2 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("EUC_CN", buffer, start, 1); + + auto const byte2{get_byte(buffer, start + 1)}; + if (not between_inc(byte2, 0xa1, 0xfe)) + PQXX_UNLIKELY + throw_for_encoding_error("EUC_CN", buffer, start, 2); + + return start + 2; +} + + +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + return next_seq_for_euc_jplike(buffer, buffer_len, start, "EUC_JP"); +} + + +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + return next_seq_for_euc_jplike(buffer, buffer_len, start, "EUC_JIS_2004"); +} + + +// https://en.wikipedia.org/wiki/Extended_Unix_Code#EUC-KR +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + if (start >= buffer_len) + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80) + return start + 1; + + if (not between_inc(byte1, 0xa1, 0xfe) or start + 2 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("EUC_KR", buffer, start, 1); + + auto const byte2{get_byte(buffer, start + 1)}; + if (not between_inc(byte2, 0xa1, 0xfe)) + PQXX_UNLIKELY + throw_for_encoding_error("EUC_KR", buffer, start, 1); + + return start + 2; +} + +// https://en.wikipedia.org/wiki/Extended_Unix_Code#EUC-TW +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + if (start >= buffer_len) + PQXX_UNLIKELY + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80) + return start + 1; + + if (start + 2 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("EUC_KR", buffer, start, 1); + + auto const byte2{get_byte(buffer, start + 1)}; + if (between_inc(byte1, 0xa1, 0xfe)) + { + if (not between_inc(byte2, 0xa1, 0xfe)) + PQXX_UNLIKELY + throw_for_encoding_error("EUC_KR", buffer, start, 2); + + return start + 2; + } + + if (byte1 != 0x8e or start + 4 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("EUC_KR", buffer, start, 1); + + if ( + between_inc(byte2, 0xa1, 0xb0) and + between_inc(get_byte(buffer, start + 2), 0xa1, 0xfe) and + between_inc(get_byte(buffer, start + 3), 0xa1, 0xfe)) + return start + 4; + + PQXX_UNLIKELY + throw_for_encoding_error("EUC_KR", buffer, start, 4); +} + +// https://en.wikipedia.org/wiki/GB_18030#Mapping +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + if (start >= buffer_len) + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80) + return start + 1; + if (byte1 == 0x80) + throw_for_encoding_error("GB18030", buffer, start, buffer_len - start); + + if (start + 2 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("GB18030", buffer, start, buffer_len - start); + + auto const byte2{get_byte(buffer, start + 1)}; + if (between_inc(byte2, 0x40, 0xfe)) + { + if (byte2 == 0x7f) + PQXX_UNLIKELY + throw_for_encoding_error("GB18030", buffer, start, 2); + + return start + 2; + } + + if (start + 4 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("GB18030", buffer, start, buffer_len - start); + + if ( + between_inc(byte2, 0x30, 0x39) and + between_inc(get_byte(buffer, start + 2), 0x81, 0xfe) and + between_inc(get_byte(buffer, start + 3), 0x30, 0x39)) + return start + 4; + + PQXX_UNLIKELY + throw_for_encoding_error("GB18030", buffer, start, 4); +} + +// https://en.wikipedia.org/wiki/GBK_(character_encoding)#Encoding +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + if (start >= buffer_len) + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80) + return start + 1; + + if (start + 2 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("GBK", buffer, start, 1); + + auto const byte2{get_byte(buffer, start + 1)}; + if ( + (between_inc(byte1, 0xa1, 0xa9) and between_inc(byte2, 0xa1, 0xfe)) or + (between_inc(byte1, 0xb0, 0xf7) and between_inc(byte2, 0xa1, 0xfe)) or + (between_inc(byte1, 0x81, 0xa0) and between_inc(byte2, 0x40, 0xfe) and + byte2 != 0x7f) or + (between_inc(byte1, 0xaa, 0xfe) and between_inc(byte2, 0x40, 0xa0) and + byte2 != 0x7f) or + (between_inc(byte1, 0xa8, 0xa9) and between_inc(byte2, 0x40, 0xa0) and + byte2 != 0x7f) or + (between_inc(byte1, 0xaa, 0xaf) and between_inc(byte2, 0xa1, 0xfe)) or + (between_inc(byte1, 0xf8, 0xfe) and between_inc(byte2, 0xa1, 0xfe)) or + (between_inc(byte1, 0xa1, 0xa7) and between_inc(byte2, 0x40, 0xa0) and + byte2 != 0x7f)) + return start + 2; + + PQXX_UNLIKELY + throw_for_encoding_error("GBK", buffer, start, 2); +} + +/* +The PostgreSQL documentation claims that the JOHAB encoding is 1-3 bytes, but +"CJKV Information Processing" describes it (actually just the Hangul portion) +as "three five-bit segments" that reside inside 16 bits (2 bytes). + +CJKV Information Processing by Ken Lunde, pg. 269: + + https://bit.ly/2BEOu5V +*/ +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + if (start >= buffer_len) + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80) + return start + 1; + + if (start + 2 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("JOHAB", buffer, start, 1); + + auto const byte2{get_byte(buffer, start)}; + if ( + (between_inc(byte1, 0x84, 0xd3) and + (between_inc(byte2, 0x41, 0x7e) or between_inc(byte2, 0x81, 0xfe))) or + ((between_inc(byte1, 0xd8, 0xde) or between_inc(byte1, 0xe0, 0xf9)) and + (between_inc(byte2, 0x31, 0x7e) or between_inc(byte2, 0x91, 0xfe)))) + return start + 2; + + PQXX_UNLIKELY + throw_for_encoding_error("JOHAB", buffer, start, 2); +} + +/* +PostgreSQL's MULE_INTERNAL is the emacs rather than Xemacs implementation; +see the server/mb/pg_wchar.h PostgreSQL header file. +This is implemented according to the description in said header file, but I was +unable to get it to successfully iterate a MULE-encoded test CSV generated +using PostgreSQL 9.2.23. Use this at your own risk. +*/ +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + if (start >= buffer_len) + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80) + return start + 1; + + if (start + 2 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("MULE_INTERNAL", buffer, start, 1); + + auto const byte2{get_byte(buffer, start + 1)}; + if (between_inc(byte1, 0x81, 0x8d) and byte2 >= 0xa0) + return start + 2; + + if (start + 3 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("MULE_INTERNAL", buffer, start, 2); + + if ( + ((byte1 == 0x9a and between_inc(byte2, 0xa0, 0xdf)) or + (byte1 == 0x9b and between_inc(byte2, 0xe0, 0xef)) or + (between_inc(byte1, 0x90, 0x99) and byte2 >= 0xa0)) and + (byte2 >= 0xa0)) + return start + 3; + + if (start + 4 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("MULE_INTERNAL", buffer, start, 3); + + if ( + ((byte1 == 0x9c and between_inc(byte2, 0xf0, 0xf4)) or + (byte1 == 0x9d and between_inc(byte2, 0xf5, 0xfe))) and + get_byte(buffer, start + 2) >= 0xa0 and + get_byte(buffer, start + 4) >= 0xa0) + return start + 4; + + PQXX_UNLIKELY + throw_for_encoding_error("MULE_INTERNAL", buffer, start, 4); +} + +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + return next_seq_for_sjislike(buffer, buffer_len, start, "SJIS"); +} + +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + return next_seq_for_sjislike(buffer, buffer_len, start, "SHIFT_JIS_2004"); +} + +// https://en.wikipedia.org/wiki/Unified_Hangul_Code +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + if (start >= buffer_len) + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80) + return start + 1; + + if (start + 2 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("UHC", buffer, start, buffer_len - start); + + auto const byte2{get_byte(buffer, start + 1)}; + if (between_inc(byte1, 0x80, 0xc6)) + { + if ( + between_inc(byte2, 0x41, 0x5a) or between_inc(byte2, 0x61, 0x7a) or + between_inc(byte2, 0x80, 0xfe)) + return start + 2; + + PQXX_UNLIKELY + throw_for_encoding_error("UHC", buffer, start, 2); + } + + if (between_inc(byte1, 0xa1, 0xfe)) + { + if (not between_inc(byte2, 0xa1, 0xfe)) + PQXX_UNLIKELY + throw_for_encoding_error("UHC", buffer, start, 2); + + return start + 2; + } + + throw_for_encoding_error("UHC", buffer, start, 1); +} + +// https://en.wikipedia.org/wiki/UTF-8#Description +template<> +PQXX_PURE std::size_t glyph_scanner::call( + char const buffer[], std::size_t buffer_len, std::size_t start) +{ + if (start >= buffer_len) + return std::string::npos; + + auto const byte1{get_byte(buffer, start)}; + if (byte1 < 0x80) + return start + 1; + + if (start + 2 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("UTF8", buffer, start, buffer_len - start); + + auto const byte2{get_byte(buffer, start + 1)}; + if (between_inc(byte1, 0xc0, 0xdf)) + { + if (not between_inc(byte2, 0x80, 0xbf)) + PQXX_UNLIKELY + throw_for_encoding_error("UTF8", buffer, start, 2); + + return start + 2; + } + + if (start + 3 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("UTF8", buffer, start, buffer_len - start); + + auto const byte3{get_byte(buffer, start + 2)}; + if (between_inc(byte1, 0xe0, 0xef)) + { + if (between_inc(byte2, 0x80, 0xbf) and between_inc(byte3, 0x80, 0xbf)) + return start + 3; + + PQXX_UNLIKELY + throw_for_encoding_error("UTF8", buffer, start, 3); + } + + if (start + 4 > buffer_len) + PQXX_UNLIKELY + throw_for_encoding_error("UTF8", buffer, start, buffer_len - start); + + if (between_inc(byte1, 0xf0, 0xf7)) + { + if ( + between_inc(byte2, 0x80, 0xbf) and between_inc(byte3, 0x80, 0xbf) and + between_inc(get_byte(buffer, start + 3), 0x80, 0xbf)) + return start + 4; + + PQXX_UNLIKELY + throw_for_encoding_error("UTF8", buffer, start, 4); + } + + PQXX_UNLIKELY + throw_for_encoding_error("UTF8", buffer, start, 1); +} + + +PQXX_PURE char const *name_encoding(int encoding_id) +{ + return pg_encoding_to_char(encoding_id); +} + + +encoding_group enc_group(int libpq_enc_id) +{ + return enc_group(name_encoding(libpq_enc_id)); +} + + +encoding_group enc_group(std::string_view encoding_name) +{ + struct mapping + { + std::string_view const name; + encoding_group const group; + constexpr mapping(std::string_view n, encoding_group g) : name{n}, group{g} + {} + constexpr bool operator<(mapping const &rhs) const + { + return name < rhs.name; + } + }; + + // C++20: Once compilers are ready, go full constexpr, leave to the compiler. + auto const sz{std::size(encoding_name)}; + if (sz > 0u) + switch (encoding_name[0]) + { + case 'B': + if (encoding_name == "BIG5"sv) + return encoding_group::BIG5; + PQXX_UNLIKELY + break; + case 'E': + // C++20: Use string_view::starts_with(). + if ((sz >= 6u) and (encoding_name.substr(0, 4) == "EUC_"sv)) + { + auto const subtype{encoding_name.substr(4)}; + static constexpr std::array subtypes{ + mapping{"CN"sv, encoding_group::EUC_CN}, + mapping{"JIS_2004"sv, encoding_group::EUC_JIS_2004}, + mapping{"JP"sv, encoding_group::EUC_JP}, + mapping{"KR"sv, encoding_group::EUC_KR}, + mapping{"TW"sv, encoding_group::EUC_TW}, + }; + for (auto const &m : subtypes) + if (m.name == subtype) + return m.group; + } + PQXX_UNLIKELY + break; + case 'G': + if (encoding_name == "GB18030"sv) + return encoding_group::GB18030; + else if (encoding_name == "GBK"sv) + return encoding_group::GBK; + PQXX_UNLIKELY + break; + case 'I': + // We know iso-8859-X, where 5 <= X < 9. They're all monobyte encodings. + if ((sz == 10) and (encoding_name.substr(0, 9) == "ISO_8859_"sv)) + { + char const subtype{encoding_name[9]}; + if (('5' <= subtype) and (subtype < '9')) + return encoding_group::MONOBYTE; + } + PQXX_UNLIKELY + break; + case 'J': + if (encoding_name == "JOHAB"sv) + return encoding_group::JOHAB; + PQXX_UNLIKELY + break; + case 'K': + if ((encoding_name == "KOI8R"sv) or (encoding_name == "KOI8U"sv)) + return encoding_group::MONOBYTE; + PQXX_UNLIKELY + break; + case 'L': + // We know LATIN1 through LATIN10. + if (encoding_name.substr(0, 5) == "LATIN"sv) + { + auto const subtype{encoding_name.substr(5)}; + if (subtype.size() == 1) + { + char const n{subtype[0]}; + if (('1' <= n) and (n <= '9')) + return encoding_group::MONOBYTE; + } + else if (subtype == "10"sv) + { + return encoding_group::MONOBYTE; + } + } + PQXX_UNLIKELY + break; + case 'M': + if (encoding_name == "MULE_INTERNAL"sv) + return encoding_group::MULE_INTERNAL; + PQXX_UNLIKELY + break; + case 'S': + if (encoding_name == "SHIFT_JIS_2004"sv) + return encoding_group::SHIFT_JIS_2004; + else if (encoding_name == "SJIS"sv) + return encoding_group::SJIS; + else if (encoding_name == "SQL_ASCII"sv) + return encoding_group::MONOBYTE; + PQXX_UNLIKELY + break; + case 'U': + if (encoding_name == "UHC"sv) + return encoding_group::UHC; + else if (encoding_name == "UTF8"sv) + return encoding_group::UTF8; + PQXX_UNLIKELY + break; + case 'W': + if (encoding_name.substr(0, 3) == "WIN"sv) + { + auto const subtype{encoding_name.substr(3)}; + static constexpr std::array subtypes{ + "866"sv, "874"sv, "1250"sv, "1251"sv, "1252"sv, "1253"sv, + "1254"sv, "1255"sv, "1256"sv, "1257"sv, "1258"sv, + }; + for (auto const n : subtypes) + if (n == subtype) + return encoding_group::MONOBYTE; + } + PQXX_UNLIKELY + break; + default: PQXX_UNLIKELY break; + } + PQXX_UNLIKELY + throw std::invalid_argument{ + internal::concat("Unrecognized encoding: '", encoding_name, "'.")}; +} + + +/// Look up instantiation @c T::call at runtime. +/** Here, "T" is a struct template with a static member function "call", whose + * type is "F". + * + * The return value is a pointer to the "call" member function for the + * instantiation of T for encoding group enc. + */ +template class T, typename F> +constexpr inline F *for_encoding(encoding_group enc) +{ +#define CASE_GROUP(ENC) \ + case encoding_group::ENC: return T::call + + switch (enc) + { + PQXX_LIKELY CASE_GROUP(MONOBYTE); + CASE_GROUP(BIG5); + CASE_GROUP(EUC_CN); + CASE_GROUP(EUC_JP); + CASE_GROUP(EUC_JIS_2004); + CASE_GROUP(EUC_KR); + CASE_GROUP(EUC_TW); + CASE_GROUP(GB18030); + CASE_GROUP(GBK); + CASE_GROUP(JOHAB); + CASE_GROUP(MULE_INTERNAL); + CASE_GROUP(SJIS); + CASE_GROUP(SHIFT_JIS_2004); + CASE_GROUP(UHC); + PQXX_LIKELY CASE_GROUP(UTF8); + } + PQXX_UNLIKELY + throw pqxx::usage_error{ + internal::concat("Unsupported encoding group code ", enc, ".")}; + +#undef CASE_GROUP +} + + +PQXX_PURE glyph_scanner_func *get_glyph_scanner(encoding_group enc) +{ + return for_encoding(enc); +} + + +template struct char_finder +{ + constexpr static PQXX_PURE std::size_t + call(std::string_view haystack, char needle, std::size_t start) + { + auto const buffer{std::data(haystack)}; + auto const size{std::size(haystack)}; + for (auto here{start}; here + 1 <= size; + here = glyph_scanner::call(buffer, size, here)) + { + if (haystack[here] == needle) + return here; + } + return std::string::npos; + } +}; + + +template struct string_finder +{ + PQXX_PURE constexpr static PQXX_PURE std::size_t + call(std::string_view haystack, std::string_view needle, std::size_t start) + { + auto const buffer{std::data(haystack)}; + auto const size{std::size(haystack)}; + auto const needle_size{std::size(needle)}; + for (auto here{start}; here + needle_size <= size; + here = glyph_scanner::call(buffer, size, here)) + { + if (std::memcmp(buffer + here, std::data(needle), needle_size) == 0) + return here; + } + return std::string::npos; + } +}; + +#undef DISPATCH_ENCODING_OPERATION +} // namespace pqxx::internal diff --git a/ext/libpqxx-7.7.3/src/errorhandler.cxx b/ext/libpqxx-7.7.3/src/errorhandler.cxx new file mode 100644 index 000000000..de120ff3d --- /dev/null +++ b/ext/libpqxx-7.7.3/src/errorhandler.cxx @@ -0,0 +1,43 @@ +/** Implementation of pqxx::errorhandler and helpers. + * + * pqxx::errorhandler allows programs to receive errors and warnings. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/connection.hxx" +#include "pqxx/errorhandler.hxx" +#include "pqxx/internal/gates/connection-errorhandler.hxx" + +#include "pqxx/internal/header-post.hxx" + + +pqxx::errorhandler::errorhandler(connection &conn) : m_home{&conn} +{ + pqxx::internal::gate::connection_errorhandler{*m_home}.register_errorhandler( + this); +} + + +pqxx::errorhandler::~errorhandler() +{ + unregister(); +} + + +void pqxx::errorhandler::unregister() noexcept +{ + if (m_home != nullptr) + { + pqxx::internal::gate::connection_errorhandler connection_gate{*m_home}; + m_home = nullptr; + connection_gate.unregister_errorhandler(this); + } +} diff --git a/ext/libpqxx-7.7.3/src/except.cxx b/ext/libpqxx-7.7.3/src/except.cxx new file mode 100644 index 000000000..4974e8ac0 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/except.cxx @@ -0,0 +1,126 @@ +/** Implementation of libpqxx exception classes. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/except.hxx" +#include "pqxx/internal/concat.hxx" + +#include "pqxx/internal/header-post.hxx" + +pqxx::failure::failure(std::string const &whatarg) : + std::runtime_error{whatarg} +{} + + +pqxx::broken_connection::broken_connection() : + failure{"Connection to database failed."} +{} + + +pqxx::broken_connection::broken_connection(std::string const &whatarg) : + failure{whatarg} +{} + + +pqxx::variable_set_to_null::variable_set_to_null() : + variable_set_to_null{ + "Attempt to set a variable to null. This is not allowed."} +{} + + +pqxx::variable_set_to_null::variable_set_to_null(std::string const &whatarg) : + failure{whatarg} +{} + + +pqxx::sql_error::sql_error( + std::string const &whatarg, std::string const &Q, char const sqlstate[]) : + failure{whatarg}, m_query{Q}, m_sqlstate{sqlstate ? sqlstate : ""} +{} + + +pqxx::sql_error::~sql_error() noexcept = default; + + +PQXX_PURE std::string const &pqxx::sql_error::query() const noexcept +{ + return m_query; +} + + +PQXX_PURE std::string const &pqxx::sql_error::sqlstate() const noexcept +{ + return m_sqlstate; +} + + +pqxx::in_doubt_error::in_doubt_error(std::string const &whatarg) : + failure{whatarg} +{} + + +pqxx::transaction_rollback::transaction_rollback( + std::string const &whatarg, std::string const &q, char const sqlstate[]) : + sql_error{whatarg, q, sqlstate} +{} + + +pqxx::serialization_failure::serialization_failure( + std::string const &whatarg, std::string const &q, char const sqlstate[]) : + transaction_rollback{whatarg, q, sqlstate} +{} + + +pqxx::statement_completion_unknown::statement_completion_unknown( + std::string const &whatarg, std::string const &q, char const sqlstate[]) : + transaction_rollback{whatarg, q, sqlstate} +{} + + +pqxx::deadlock_detected::deadlock_detected( + std::string const &whatarg, std::string const &q, char const sqlstate[]) : + transaction_rollback{whatarg, q, sqlstate} +{} + + +pqxx::internal_error::internal_error(std::string const &whatarg) : + std::logic_error{internal::concat("libpqxx internal error: ", whatarg)} +{} + + +pqxx::usage_error::usage_error(std::string const &whatarg) : + std::logic_error{whatarg} +{} + + +pqxx::argument_error::argument_error(std::string const &whatarg) : + invalid_argument{whatarg} +{} + + +pqxx::conversion_error::conversion_error(std::string const &whatarg) : + domain_error{whatarg} +{} + + +pqxx::conversion_overrun::conversion_overrun(std::string const &whatarg) : + conversion_error{whatarg} +{} + + +pqxx::range_error::range_error(std::string const &whatarg) : + out_of_range{whatarg} +{} + + +pqxx::blob_already_exists::blob_already_exists(std::string const &whatarg) : + failure{whatarg} +{} diff --git a/ext/libpqxx-7.7.3/src/field.cxx b/ext/libpqxx-7.7.3/src/field.cxx new file mode 100644 index 000000000..f5026ced2 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/field.cxx @@ -0,0 +1,80 @@ +/** Implementation of the pqxx::field class. + * + * pqxx::field refers to a field in a query result. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/field.hxx" +#include "pqxx/internal/libpq-forward.hxx" +#include "pqxx/result.hxx" +#include "pqxx/row.hxx" + +#include "pqxx/internal/header-post.hxx" + + +pqxx::field::field(pqxx::row const &r, pqxx::row::size_type c) noexcept : + m_col{c}, m_home{r.m_result}, m_row{r.m_index} +{} + + +bool PQXX_COLD pqxx::field::operator==(field const &rhs) const +{ + if (is_null() and rhs.is_null()) + return true; + if (is_null() != rhs.is_null()) + return false; + auto const s{size()}; + return (s == std::size(rhs)) and (std::memcmp(c_str(), rhs.c_str(), s) == 0); +} + + +char const *pqxx::field::name() const & +{ + return home().column_name(col()); +} + + +pqxx::oid pqxx::field::type() const +{ + return home().column_type(col()); +} + + +pqxx::oid pqxx::field::table() const +{ + return home().column_table(col()); +} + + +pqxx::row::size_type pqxx::field::table_column() const +{ + return home().table_column(col()); +} + + +char const *pqxx::field::c_str() const & +{ + return home().get_value(idx(), col()); +} + + +bool pqxx::field::is_null() const noexcept +{ + return home().get_is_null(idx(), col()); +} + + +pqxx::field::size_type pqxx::field::size() const noexcept +{ + return home().get_length(idx(), col()); +} diff --git a/ext/libpqxx-7.7.3/src/largeobject.cxx b/ext/libpqxx-7.7.3/src/largeobject.cxx new file mode 100644 index 000000000..c57de3c73 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/largeobject.cxx @@ -0,0 +1,322 @@ +/** Implementation of the Large Objects interface. + * + * Allows direct access to large objects, as well as though I/O streams. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include +#include + +extern "C" +{ +#include +} + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/connection.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/gates/connection-largeobject.hxx" +#include "pqxx/largeobject.hxx" + +#include "pqxx/internal/header-post.hxx" + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + +namespace +{ +constexpr inline int PQXX_COLD std_mode_to_pq_mode(std::ios::openmode mode) +{ + /// Mode bits, copied from libpq-fs.h so that we no longer need that header. + constexpr int INV_WRITE{0x00020000}, INV_READ{0x00040000}; + + return ((mode & std::ios::in) ? INV_READ : 0) | + ((mode & std::ios::out) ? INV_WRITE : 0); +} + + +constexpr int PQXX_COLD std_dir_to_pq_dir(std::ios::seekdir dir) noexcept +{ + if constexpr ( + static_cast(std::ios::beg) == int(SEEK_SET) and + static_cast(std::ios::cur) == int(SEEK_CUR) and + static_cast(std::ios::end) == int(SEEK_END)) + { + // Easy optimisation: they're the same constants. This is actually the + // case for the gcc I'm using. + return dir; + } + else + switch (dir) + { + case std::ios::beg: return SEEK_SET; break; + case std::ios::cur: return SEEK_CUR; break; + case std::ios::end: return SEEK_END; break; + + // Shouldn't happen, but may silence compiler warning. + default: return dir; break; + } +} +} // namespace + + +PQXX_COLD pqxx::largeobject::largeobject(dbtransaction &t) +{ + // (Mode is ignored as of postgres 8.1.) + m_id = lo_creat(raw_connection(t), 0); + if (m_id == oid_none) + { + int const err{errno}; + if (err == ENOMEM) + throw std::bad_alloc{}; + throw failure{internal::concat( + "Could not create large object: ", reason(t.conn(), err))}; + } +} + + +PQXX_COLD +pqxx::largeobject::largeobject(dbtransaction &t, std::string_view file) +{ + m_id = lo_import(raw_connection(t), std::data(file)); + if (m_id == oid_none) + { + int const err{errno}; + if (err == ENOMEM) + throw std::bad_alloc{}; + throw failure{internal::concat( + "Could not import file '", file, + "' to large object: ", reason(t.conn(), err))}; + } +} + + +PQXX_COLD pqxx::largeobject::largeobject(largeobjectaccess const &o) noexcept : + m_id{o.id()} +{} + + +void PQXX_COLD +pqxx::largeobject::to_file(dbtransaction &t, std::string_view file) const +{ + if (id() == oid_none) + throw usage_error{"No object selected."}; + if (lo_export(raw_connection(t), id(), std::data(file)) == -1) + { + int const err{errno}; + if (err == ENOMEM) + throw std::bad_alloc{}; + throw failure{internal::concat( + "Could not export large object ", m_id, " to file '", file, + "': ", reason(t.conn(), err))}; + } +} + + +void PQXX_COLD pqxx::largeobject::remove(dbtransaction &t) const +{ + if (id() == oid_none) + throw usage_error{"No object selected."}; + if (lo_unlink(raw_connection(t), id()) == -1) + { + int const err{errno}; + if (err == ENOMEM) + throw std::bad_alloc{}; + throw failure{internal::concat( + "Could not delete large object ", m_id, ": ", reason(t.conn(), err))}; + } +} + + +pqxx::internal::pq::PGconn *PQXX_COLD +pqxx::largeobject::raw_connection(dbtransaction const &t) +{ + return pqxx::internal::gate::connection_largeobject{t.conn()} + .raw_connection(); +} + + +std::string PQXX_COLD +pqxx::largeobject::reason(connection const &c, int err) const +{ + if (err == ENOMEM) + return "Out of memory"; + return pqxx::internal::gate::const_connection_largeobject{c}.error_message(); +} + + +PQXX_COLD +pqxx::largeobjectaccess::largeobjectaccess(dbtransaction &t, openmode mode) : + largeobject{t}, m_trans{t} +{ + open(mode); +} + + +PQXX_COLD pqxx::largeobjectaccess::largeobjectaccess( + dbtransaction &t, oid o, openmode mode) : + largeobject{o}, m_trans{t} +{ + open(mode); +} + + +PQXX_COLD pqxx::largeobjectaccess::largeobjectaccess( + dbtransaction &t, largeobject o, openmode mode) : + largeobject{o}, m_trans{t} +{ + open(mode); +} + + +PQXX_COLD pqxx::largeobjectaccess::largeobjectaccess( + dbtransaction &t, std::string_view file, openmode mode) : + largeobject{t, file}, m_trans{t} +{ + open(mode); +} + + +pqxx::largeobjectaccess::size_type PQXX_COLD +pqxx::largeobjectaccess::seek(size_type dest, seekdir dir) +{ + auto const res{cseek(dest, dir)}; + if (res == -1) + { + int const err{errno}; + if (err == ENOMEM) + throw std::bad_alloc{}; + if (id() == oid_none) + throw usage_error{"No object selected."}; + throw failure{ + internal::concat("Error seeking in large object: ", reason(err))}; + } + + return res; +} + + +pqxx::largeobjectaccess::pos_type PQXX_COLD +pqxx::largeobjectaccess::cseek(off_type dest, seekdir dir) noexcept +{ + return lo_lseek64(raw_connection(), m_fd, dest, std_dir_to_pq_dir(dir)); +} + + +pqxx::largeobjectaccess::pos_type PQXX_COLD +pqxx::largeobjectaccess::cwrite(char const buf[], std::size_t len) noexcept +{ + return std::max(lo_write(raw_connection(), m_fd, buf, len), -1); +} + + +pqxx::largeobjectaccess::pos_type PQXX_COLD +pqxx::largeobjectaccess::cread(char buf[], std::size_t len) noexcept +{ + return std::max(lo_read(raw_connection(), m_fd, buf, len), -1); +} + + +pqxx::largeobjectaccess::pos_type PQXX_COLD +pqxx::largeobjectaccess::ctell() const noexcept +{ + return lo_tell64(raw_connection(), m_fd); +} + + +void PQXX_COLD +pqxx::largeobjectaccess::write(char const buf[], std::size_t len) +{ + if (id() == oid_none) + throw usage_error{"No object selected."}; + if (auto const bytes{cwrite(buf, len)}; internal::cmp_less(bytes, len)) + { + int const err{errno}; + if (err == ENOMEM) + throw std::bad_alloc{}; + if (bytes < 0) + throw failure{internal::concat( + "Error writing to large object #", id(), ": ", reason(err))}; + if (bytes == 0) + throw failure{internal::concat( + "Could not write to large object #", id(), ": ", reason(err))}; + + throw failure{internal::concat( + "Wanted to write ", len, " bytes to large object #", id(), + "; could only write ", bytes, ".")}; + } +} + + +pqxx::largeobjectaccess::size_type PQXX_COLD +pqxx::largeobjectaccess::read(char buf[], std::size_t len) +{ + if (id() == oid_none) + throw usage_error{"No object selected."}; + auto const bytes{cread(buf, len)}; + if (bytes < 0) + { + int const err{errno}; + if (err == ENOMEM) + throw std::bad_alloc{}; + throw failure{internal::concat( + "Error reading from large object #", id(), ": ", reason(err))}; + } + return bytes; +} + + +void PQXX_COLD pqxx::largeobjectaccess::open(openmode mode) +{ + if (id() == oid_none) + throw usage_error{"No object selected."}; + m_fd = lo_open(raw_connection(), id(), std_mode_to_pq_mode(mode)); + if (m_fd < 0) + { + int const err{errno}; + if (err == ENOMEM) + throw std::bad_alloc{}; + throw failure{internal::concat( + "Could not open large object ", id(), ": ", reason(err))}; + } +} + + +void PQXX_COLD pqxx::largeobjectaccess::close() noexcept +{ + if (m_fd >= 0) + lo_close(raw_connection(), m_fd); +} + + +pqxx::largeobjectaccess::size_type PQXX_COLD +pqxx::largeobjectaccess::tell() const +{ + auto const res{ctell()}; + if (res == -1) + throw failure{reason(errno)}; + return res; +} + + +std::string PQXX_COLD pqxx::largeobjectaccess::reason(int err) const +{ + if (m_fd == -1) + return "No object opened."; + return largeobject::reason(m_trans.conn(), err); +} + + +void PQXX_COLD pqxx::largeobjectaccess::process_notice(zview s) noexcept +{ + m_trans.process_notice(s); +} + +#include "pqxx/internal/ignore-deprecated-post.hxx" diff --git a/ext/libpqxx-7.7.3/src/notification.cxx b/ext/libpqxx-7.7.3/src/notification.cxx new file mode 100644 index 000000000..eb8d5e1a1 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/notification.cxx @@ -0,0 +1,35 @@ +/** Implementation of the pqxx::notification_receiever class. + * + * pqxx::notification_receiver processes notifications. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/internal/gates/connection-notification_receiver.hxx" +#include "pqxx/notification.hxx" + +#include "pqxx/internal/header-post.hxx" + + +pqxx::notification_receiver::notification_receiver( + connection &c, std::string_view channel) : + m_conn{c}, m_channel{channel} +{ + pqxx::internal::gate::connection_notification_receiver{c}.add_receiver(this); +} + + +pqxx::notification_receiver::~notification_receiver() +{ + pqxx::internal::gate::connection_notification_receiver{this->conn()} + .remove_receiver(this); +} diff --git a/ext/libpqxx-7.7.3/src/params.cxx b/ext/libpqxx-7.7.3/src/params.cxx new file mode 100644 index 000000000..bd3fc108c --- /dev/null +++ b/ext/libpqxx-7.7.3/src/params.cxx @@ -0,0 +1,122 @@ +/* Implementations related to prepared and parameterised statements. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/params.hxx" + +#include "pqxx/internal/header-post.hxx" + + +void pqxx::internal::c_params::reserve(std::size_t n) & +{ + values.reserve(n); + lengths.reserve(n); + formats.reserve(n); +} + + +void pqxx::params::reserve(std::size_t n) & +{ + m_params.reserve(n); +} + + +void pqxx::params::append() & +{ + m_params.emplace_back(nullptr); +} + + +void pqxx::params::append(zview value) & +{ + m_params.emplace_back(value); +} + + +void pqxx::params::append(std::string const &value) & +{ + m_params.emplace_back(value); +} + + +void pqxx::params::append(std::string &&value) & +{ + m_params.emplace_back(std::move(value)); +} + + +void pqxx::params::append(params const &value) & +{ + this->reserve(std::size(value.m_params) + std::size(this->m_params)); + for (auto const ¶m : value.m_params) m_params.emplace_back(param); +} + + +void pqxx::params::append(std::basic_string_view value) & +{ + m_params.emplace_back(value); +} + + +void pqxx::params::append(std::basic_string const &value) & +{ + m_params.emplace_back(value); +} + + +void pqxx::params::append(std::basic_string &&value) & +{ + m_params.emplace_back(std::move(value)); +} + + +void PQXX_COLD pqxx::params::append(binarystring const &value) & +{ + m_params.push_back(entry{value.bytes_view()}); +} + + +void pqxx::params::append(params &&value) & +{ + this->reserve(std::size(value.m_params) + std::size(this->m_params)); + for (auto const ¶m : value.m_params) + m_params.emplace_back(std::move(param)); + value.m_params.clear(); +} + + +pqxx::internal::c_params pqxx::params::make_c_params() const +{ + pqxx::internal::c_params p; + p.reserve(std::size(m_params)); + for (auto const ¶m : m_params) + std::visit( + [&p](auto const &value) { + using T = strip_t; + + if constexpr (std::is_same_v) + { + p.values.push_back(nullptr); + p.lengths.push_back(0); + } + else + { + p.values.push_back(reinterpret_cast(std::data(value))); + p.lengths.push_back( + check_cast(internal::ssize(value), s_overflow)); + } + + p.formats.push_back(param_format(value)); + }, + param); + + return p; +} diff --git a/ext/libpqxx-7.7.3/src/pipeline.cxx b/ext/libpqxx-7.7.3/src/pipeline.cxx new file mode 100644 index 000000000..854b1a70e --- /dev/null +++ b/ext/libpqxx-7.7.3/src/pipeline.cxx @@ -0,0 +1,448 @@ +/** Implementation of the pqxx::pipeline class. + * + * Throughput-optimized query interface. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/dbtransaction.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/gates/connection-pipeline.hxx" +#include "pqxx/internal/gates/result-creation.hxx" +#include "pqxx/internal/gates/result-pipeline.hxx" +#include "pqxx/pipeline.hxx" +#include "pqxx/separated_list.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace +{ +std::string const theSeparator{"; "}; +std::string const theDummyValue{"1"}; +std::string const theDummyQuery{"SELECT " + theDummyValue + theSeparator}; +} // namespace + + +void pqxx::pipeline::init() +{ + m_encoding = internal::enc_group(m_trans.conn().encoding_id()); + m_issuedrange = make_pair(std::end(m_queries), std::end(m_queries)); + attach(); +} + + +pqxx::pipeline::~pipeline() noexcept +{ + try + { + cancel(); + } + catch (std::exception const &) + {} + detach(); +} + + +void pqxx::pipeline::attach() +{ + if (not registered()) + register_me(); +} + + +void pqxx::pipeline::detach() +{ + if (registered()) + unregister_me(); +} + + +pqxx::pipeline::query_id pqxx::pipeline::insert(std::string_view q) & +{ + attach(); + query_id const qid{generate_id()}; + auto const i{m_queries.insert(std::make_pair(qid, Query(q))).first}; + + if (m_issuedrange.second == std::end(m_queries)) + { + m_issuedrange.second = i; + if (m_issuedrange.first == std::end(m_queries)) + m_issuedrange.first = i; + } + m_num_waiting++; + + if (m_num_waiting > m_retain) + { + if (have_pending()) + receive_if_available(); + if (not have_pending()) + issue(); + } + + return qid; +} + + +void pqxx::pipeline::complete() +{ + if (have_pending()) + receive(m_issuedrange.second); + if (m_num_waiting and (m_error == qid_limit())) + { + issue(); + receive(std::end(m_queries)); + } + detach(); +} + + +void pqxx::pipeline::flush() +{ + if (not std::empty(m_queries)) + { + if (have_pending()) + receive(m_issuedrange.second); + m_issuedrange.first = m_issuedrange.second = std::end(m_queries); + m_num_waiting = 0; + m_dummy_pending = false; + m_queries.clear(); + } + detach(); +} + + +void PQXX_COLD pqxx::pipeline::cancel() +{ + while (have_pending()) + { + pqxx::internal::gate::connection_pipeline(m_trans.conn()).cancel_query(); + auto canceled_query{m_issuedrange.first}; + ++m_issuedrange.first; + m_queries.erase(canceled_query); + } +} + + +bool pqxx::pipeline::is_finished(pipeline::query_id q) const +{ + if (m_queries.find(q) == std::end(m_queries)) + throw std::logic_error{ + internal::concat("Requested status for unknown query '", q, "'.")}; + return (QueryMap::const_iterator(m_issuedrange.first) == + std::end(m_queries)) or + (q < m_issuedrange.first->first and q < m_error); +} + + +std::pair pqxx::pipeline::retrieve() +{ + if (std::empty(m_queries)) + throw std::logic_error{"Attempt to retrieve result from empty pipeline."}; + return retrieve(std::begin(m_queries)); +} + + +int pqxx::pipeline::retain(int retain_max) & +{ + if (retain_max < 0) + throw range_error{internal::concat( + "Attempt to make pipeline retain ", retain_max, " queries")}; + + int const oldvalue{m_retain}; + m_retain = retain_max; + + if (m_num_waiting >= m_retain) + resume(); + + return oldvalue; +} + + +void pqxx::pipeline::resume() & +{ + if (have_pending()) + receive_if_available(); + if (not have_pending() and m_num_waiting) + { + issue(); + receive_if_available(); + } +} + + +pqxx::pipeline::query_id pqxx::pipeline::generate_id() +{ + if (m_q_id == qid_limit()) + throw std::overflow_error{"Too many queries went through pipeline."}; + ++m_q_id; + return m_q_id; +} + + +void pqxx::pipeline::issue() +{ + // Retrieve that null result for the last query, if needed. + obtain_result(); + + // Don't issue anything if we've encountered an error. + if (m_error < qid_limit()) + return; + + // Start with oldest query (lowest id) not in previous issue range. + auto oldest{m_issuedrange.second}; + + // Construct cumulative query string for entire batch. + auto cum{separated_list( + theSeparator, oldest, std::end(m_queries), + [](QueryMap::const_iterator i) { return i->second.query; })}; + auto const num_issued{ + QueryMap::size_type(std::distance(oldest, std::end(m_queries)))}; + bool const prepend_dummy{num_issued > 1}; + if (prepend_dummy) + cum = theDummyQuery + cum; + + pqxx::internal::gate::connection_pipeline{m_trans.conn()}.start_exec( + cum.c_str()); + + // Since we managed to send out these queries, update state to reflect this. + m_dummy_pending = prepend_dummy; + m_issuedrange.first = oldest; + m_issuedrange.second = std::end(m_queries); + m_num_waiting -= check_cast(num_issued, "pipeline issue()"sv); +} + + +void PQXX_COLD pqxx::pipeline::internal_error(std::string const &err) +{ + set_error_at(0); + throw pqxx::internal_error{err}; +} + + +bool pqxx::pipeline::obtain_result(bool expect_none) +{ + pqxx::internal::gate::connection_pipeline gate{m_trans.conn()}; + auto const r{gate.get_result()}; + if (r == nullptr) + { + if (have_pending() and not expect_none) + { + PQXX_UNLIKELY + set_error_at(m_issuedrange.first->first); + m_issuedrange.second = m_issuedrange.first; + } + return false; + } + + result const res{pqxx::internal::gate::result_creation::create( + r, std::begin(m_queries)->second.query, m_encoding)}; + + if (not have_pending()) + { + PQXX_UNLIKELY + set_error_at(std::begin(m_queries)->first); + throw std::logic_error{ + "Got more results from pipeline than there were queries."}; + } + + // Must be the result for the oldest pending query. + if (not std::empty(m_issuedrange.first->second.res)) + PQXX_UNLIKELY + internal_error("Multiple results for one query."); + + m_issuedrange.first->second.res = res; + ++m_issuedrange.first; + + return true; +} + + +void pqxx::pipeline::obtain_dummy() +{ + // Allocate once, re-use across invocations. + static auto const text{ + std::make_shared("[DUMMY PIPELINE QUERY]")}; + + pqxx::internal::gate::connection_pipeline gate{m_trans.conn()}; + auto const r{gate.get_result()}; + m_dummy_pending = false; + + if (r == nullptr) + PQXX_UNLIKELY + internal_error("Pipeline got no result from backend when it expected one."); + + result R{pqxx::internal::gate::result_creation::create(r, text, m_encoding)}; + + bool OK{false}; + try + { + pqxx::internal::gate::result_creation{R}.check_status(); + OK = true; + } + catch (sql_error const &) + {} + if (OK) + { + PQXX_LIKELY + if (std::size(R) > 1) + PQXX_UNLIKELY + internal_error("Unexpected result for dummy query in pipeline."); + + if (R.at(0).at(0).as() != theDummyValue) + PQXX_UNLIKELY + internal_error("Dummy query in pipeline returned unexpected value."); + return; + } + + // TODO: Can we actually re-issue statements after a failure? + /* Execution of this batch failed. + * + * When we send multiple statements in one go, the backend treats them as a + * single transaction. So the entire batch was effectively rolled back. + * + * Since none of the queries in the batch were actually executed, we can + * afford to replay them one by one until we find the exact query that + * caused the error. This gives us not only a more specific error message + * to report, but also tells us which query to report it for. + */ + // First, give the whole batch the same syntax error message, in case all + // else is going to fail. + for (auto i{m_issuedrange.first}; i != m_issuedrange.second; ++i) + i->second.res = R; + + // Remember where the end of this batch was + auto const stop{m_issuedrange.second}; + + // Retrieve that null result for the last query, if needed + obtain_result(true); + + // Reset internal state to forget botched batch attempt + m_num_waiting += check_cast( + std::distance(m_issuedrange.first, stop), "pipeline obtain_dummy()"sv); + m_issuedrange.second = m_issuedrange.first; + + // Issue queries in failed batch one at a time. + unregister_me(); + try + { + do { + m_num_waiting--; + auto const query{*m_issuedrange.first->second.query}; + auto &holder{m_issuedrange.first->second}; + holder.res = m_trans.exec(query); + pqxx::internal::gate::result_creation{holder.res}.check_status(); + ++m_issuedrange.first; + } while (m_issuedrange.first != stop); + } + catch (std::exception const &) + { + auto const thud{m_issuedrange.first->first}; + ++m_issuedrange.first; + m_issuedrange.second = m_issuedrange.first; + auto q{m_issuedrange.first}; + set_error_at((q == std::end(m_queries)) ? thud + 1 : q->first); + } +} + + +std::pair +pqxx::pipeline::retrieve(pipeline::QueryMap::iterator q) +{ + if (q == std::end(m_queries)) + throw std::logic_error{"Attempt to retrieve result for unknown query."}; + + if (q->first >= m_error) + throw std::runtime_error{ + "Could not complete query in pipeline due to error in earlier query."}; + + // If query hasn't issued yet, do it now. + if ( + m_issuedrange.second != std::end(m_queries) and + (q->first >= m_issuedrange.second->first)) + { + if (have_pending()) + receive(m_issuedrange.second); + if (m_error == qid_limit()) + issue(); + } + + // If result not in yet, get it; else get at least whatever's convenient. + if (have_pending()) + { + if (q->first >= m_issuedrange.first->first) + { + auto suc{q}; + ++suc; + receive(suc); + } + else + { + receive_if_available(); + } + } + + if (q->first >= m_error) + throw std::runtime_error{ + "Could not complete query in pipeline due to error in earlier query."}; + + // Don't leave the backend idle if there are queries waiting to be issued. + if (m_num_waiting and not have_pending() and (m_error == qid_limit())) + issue(); + + result const R{q->second.res}; + auto const P{std::make_pair(q->first, R)}; + + m_queries.erase(q); + + pqxx::internal::gate::result_creation{R}.check_status(); + return P; +} + + +void pqxx::pipeline::get_further_available_results() +{ + pqxx::internal::gate::connection_pipeline gate{m_trans.conn()}; + while (not gate.is_busy() and obtain_result()) + if (not gate.consume_input()) + throw broken_connection{}; +} + + +void pqxx::pipeline::receive_if_available() +{ + pqxx::internal::gate::connection_pipeline gate{m_trans.conn()}; + if (not gate.consume_input()) + throw broken_connection{}; + if (gate.is_busy()) + return; + + if (m_dummy_pending) + obtain_dummy(); + if (have_pending()) + get_further_available_results(); +} + + +void pqxx::pipeline::receive(pipeline::QueryMap::const_iterator stop) +{ + if (m_dummy_pending) + obtain_dummy(); + + while (obtain_result() and + QueryMap::const_iterator{m_issuedrange.first} != stop) + ; + + // Also haul in any remaining "targets of opportunity". + if (QueryMap::const_iterator{m_issuedrange.first} == stop) + get_further_available_results(); +} diff --git a/ext/libpqxx-7.7.3/src/pqxx-source.hxx b/ext/libpqxx-7.7.3/src/pqxx-source.hxx new file mode 100644 index 000000000..1a8f5fb11 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/pqxx-source.hxx @@ -0,0 +1,30 @@ +/* Compiler settings for compiling libpqxx itself. + * + * Include this header in every source file that goes into the libpqxx library + * binary, and nowhere else. + * + * To ensure this, include this file once, as the very first header, in each + * compilation unit for the library. + * + * DO NOT INCLUDE THIS FILE when building client programs. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ + +// Workarounds & definitions needed to compile libpqxx into a library. +#include "pqxx/config-internal-compiler.h" + +#ifdef _WIN32 + +# ifdef PQXX_SHARED +// We're building libpqxx as a shared library. +# undef PQXX_LIBEXPORT +# define PQXX_LIBEXPORT __declspec(dllexport) +# define PQXX_PRIVATE __declspec() +# endif // PQXX_SHARED + +#endif // _WIN32 diff --git a/ext/libpqxx-7.7.3/src/result.cxx b/ext/libpqxx-7.7.3/src/result.cxx new file mode 100644 index 000000000..86a739004 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/result.cxx @@ -0,0 +1,536 @@ +/** Implementation of the pqxx::result class and support classes. + * + * pqxx::result represents the set of result rows from a database query + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include +#include + +extern "C" +{ +#include +} + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/except.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/result_iterator.hxx" +#include "pqxx/result.hxx" +#include "pqxx/row.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace pqxx +{ +PQXX_DECLARE_ENUM_CONVERSION(ExecStatusType); +} + +std::string const pqxx::result::s_empty_string; + + +/// C++ wrapper for libpq's PQclear. +void pqxx::internal::clear_result(pq::PGresult const *data) +{ + PQclear(const_cast(data)); +} + + +pqxx::result::result( + pqxx::internal::pq::PGresult *rhs, std::shared_ptr query, + internal::encoding_group enc) : + m_data{make_data_pointer(rhs)}, m_query{query}, m_encoding(enc) +{} + + +bool pqxx::result::operator==(result const &rhs) const noexcept +{ + if (&rhs == this) + PQXX_UNLIKELY return true; + auto const s{size()}; + if (std::size(rhs) != s) + return false; + for (size_type i{0}; i < s; ++i) + if ((*this)[i] != rhs[i]) + return false; + return true; +} + + +pqxx::result::const_reverse_iterator pqxx::result::rbegin() const +{ + return const_reverse_iterator{end()}; +} + + +pqxx::result::const_reverse_iterator pqxx::result::crbegin() const +{ + return rbegin(); +} + + +pqxx::result::const_reverse_iterator pqxx::result::rend() const +{ + return const_reverse_iterator{begin()}; +} + + +pqxx::result::const_reverse_iterator pqxx::result::crend() const +{ + return rend(); +} + + +pqxx::result::const_iterator pqxx::result::begin() const noexcept +{ + return {this, 0}; +} + + +pqxx::result::const_iterator pqxx::result::cbegin() const noexcept +{ + return begin(); +} + + +pqxx::result::size_type pqxx::result::size() const noexcept +{ + return (m_data.get() == nullptr) ? + 0 : + static_cast(PQntuples(m_data.get())); +} + + +bool pqxx::result::empty() const noexcept +{ + return (m_data.get() == nullptr) or (PQntuples(m_data.get()) == 0); +} + + +pqxx::result::reference pqxx::result::front() const noexcept +{ + return row{*this, 0, columns()}; +} + + +pqxx::result::reference pqxx::result::back() const noexcept +{ + return row{*this, size() - 1, columns()}; +} + + +void pqxx::result::swap(result &rhs) noexcept +{ + m_data.swap(rhs.m_data); + m_query.swap(rhs.m_query); +} + + +pqxx::row pqxx::result::operator[](result_size_type i) const noexcept +{ + return row{*this, i, columns()}; +} + + +#if defined(PQXX_HAVE_MULTIDIMENSIONAL_SUBSCRIPT) +pqxx::field pqxx::result::operator[]( + result_size_type row_num, row_size_type col_num) const noexcept +{ + return {*this, row_num, field_num}; +} +#endif + + +pqxx::row pqxx::result::at(pqxx::result::size_type i) const +{ + if (i >= size()) + throw range_error{"Row number out of range."}; + return operator[](i); +} + + +pqxx::field pqxx::result::at( + pqxx::result_size_type row_num, pqxx::row_size_type col_num) const +{ + if (row_num >= size()) + throw range_error{"Row number out of range."}; + if (col_num >= columns()) + throw range_error{"Column out of range."}; + return {*this, row_num, col_num}; +} + + +namespace +{ +/// C string comparison. +inline bool equal(char const lhs[], char const rhs[]) +{ + return strcmp(lhs, rhs) == 0; +} +} // namespace + +void PQXX_COLD pqxx::result::throw_sql_error( + std::string const &Err, std::string const &Query) const +{ + // Try to establish more precise error type, and throw corresponding + // type of exception. + char const *const code{PQresultErrorField(m_data.get(), PG_DIAG_SQLSTATE)}; + if (code == nullptr) + { + // No SQLSTATE at all. Can this even happen? + // Let's assume the connection is no longer usable. + throw broken_connection{Err}; + } + + switch (code[0]) + { + PQXX_UNLIKELY + case '\0': + // SQLSTATE is empty. We may have seen this happen in one + // circumstance: a client-side socket timeout (while using the + // tcp_user_timeout connection option). Unfortunately in that case the + // connection was just fine, so we had no real way of detecting the + // problem. (Trying to continue to use the connection does break + // though, so I feel justified in panicking.) + throw broken_connection{Err}; + + case '0': + switch (code[1]) + { + case 'A': throw feature_not_supported{Err, Query, code}; + case '8': throw broken_connection{Err}; + case 'L': + case 'P': throw insufficient_privilege{Err, Query, code}; + } + break; + case '2': + switch (code[1]) + { + case '2': throw data_exception{Err, Query, code}; + case '3': + if (equal(code, "23001")) + throw restrict_violation{Err, Query, code}; + if (equal(code, "23502")) + throw not_null_violation{Err, Query, code}; + if (equal(code, "23503")) + throw foreign_key_violation{Err, Query, code}; + if (equal(code, "23505")) + throw unique_violation{Err, Query, code}; + if (equal(code, "23514")) + throw check_violation{Err, Query, code}; + throw integrity_constraint_violation{Err, Query, code}; + case '4': throw invalid_cursor_state{Err, Query, code}; + case '6': throw invalid_sql_statement_name{Err, Query, code}; + } + break; + case '3': + switch (code[1]) + { + case '4': throw invalid_cursor_name{Err, Query, code}; + } + break; + case '4': + switch (code[1]) + { + case '0': + if (equal(code, "40000")) + throw transaction_rollback{Err, Query, code}; + if (equal(code, "40001")) + throw serialization_failure{Err, Query, code}; + if (equal(code, "40003")) + throw statement_completion_unknown{Err, Query, code}; + if (equal(code, "40P01")) + throw deadlock_detected{Err, Query, code}; + break; + case '2': + if (equal(code, "42501")) + throw insufficient_privilege{Err, Query}; + if (equal(code, "42601")) + throw syntax_error{Err, Query, code, errorposition()}; + if (equal(code, "42703")) + throw undefined_column{Err, Query, code}; + if (equal(code, "42883")) + throw undefined_function{Err, Query, code}; + if (equal(code, "42P01")) + throw undefined_table{Err, Query, code}; + } + break; + case '5': + switch (code[1]) + { + case '3': + if (equal(code, "53100")) + throw disk_full{Err, Query, code}; + if (equal(code, "53200")) + throw out_of_memory{Err, Query, code}; + if (equal(code, "53300")) + throw too_many_connections{Err}; + throw insufficient_resources{Err, Query, code}; + } + break; + + case 'P': + if (equal(code, "P0001")) + throw plpgsql_raise{Err, Query, code}; + if (equal(code, "P0002")) + throw plpgsql_no_data_found{Err, Query, code}; + if (equal(code, "P0003")) + throw plpgsql_too_many_rows{Err, Query, code}; + throw plpgsql_error{Err, Query, code}; + } + + // Unknown error code. + throw sql_error{Err, Query, code}; +} + +void pqxx::result::check_status(std::string_view desc) const +{ + if (auto err{status_error()}; not std::empty(err)) + { + PQXX_UNLIKELY + if (not std::empty(desc)) + err = pqxx::internal::concat("Failure during '", desc, "': ", err); + throw_sql_error(err, query()); + } +} + + +std::string pqxx::result::status_error() const +{ + if (m_data.get() == nullptr) + throw failure{"No result set given."}; + + std::string err; + + switch (PQresultStatus(m_data.get())) + { + case PGRES_EMPTY_QUERY: // The string sent to the backend was empty. + case PGRES_COMMAND_OK: // Successful completion, no result data. + case PGRES_TUPLES_OK: // The query successfully executed. + break; + + case PGRES_COPY_OUT: // Copy Out (from server) data transfer started. + case PGRES_COPY_IN: // Copy In (to server) data transfer started. + break; + + case PGRES_BAD_RESPONSE: // The server's response was not understood. + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: err = PQresultErrorMessage(m_data.get()); break; + + default: + throw internal_error{internal::concat( + "pqxx::result: Unrecognized response code ", + PQresultStatus(m_data.get()))}; + } + return err; +} + + +char const *pqxx::result::cmd_status() const noexcept +{ + return PQcmdStatus(const_cast(m_data.get())); +} + + +std::string const &pqxx::result::query() const &noexcept +{ + return (m_query.get() == nullptr) ? s_empty_string : *m_query; +} + + +pqxx::oid pqxx::result::inserted_oid() const +{ + if (m_data.get() == nullptr) + throw usage_error{ + "Attempt to read oid of inserted row without an INSERT result"}; + return PQoidValue(const_cast(m_data.get())); +} + + +pqxx::result::size_type pqxx::result::affected_rows() const +{ + auto const rows_str{ + PQcmdTuples(const_cast(m_data.get()))}; + return (rows_str[0] == '\0') ? 0 : size_type(atoi(rows_str)); +} + + +char const *pqxx::result::get_value( + pqxx::result::size_type row, pqxx::row::size_type col) const +{ + return PQgetvalue(m_data.get(), row, col); +} + + +bool pqxx::result::get_is_null( + pqxx::result::size_type row, pqxx::row::size_type col) const +{ + return PQgetisnull(m_data.get(), row, col) != 0; +} + +pqxx::field::size_type pqxx::result::get_length( + pqxx::result::size_type row, pqxx::row::size_type col) const noexcept +{ + return static_cast( + PQgetlength(m_data.get(), row, col)); +} + + +pqxx::oid pqxx::result::column_type(row::size_type col_num) const +{ + oid const t{PQftype(m_data.get(), col_num)}; + if (t == oid_none) + throw argument_error{internal::concat( + "Attempt to retrieve type of nonexistent column ", col_num, + " of query result.")}; + return t; +} + + +pqxx::row::size_type pqxx::result::column_number(zview col_name) const +{ + auto const n{PQfnumber( + const_cast(m_data.get()), col_name.c_str())}; + if (n == -1) + throw argument_error{ + internal::concat("Unknown column name: '", col_name, "'.")}; + + return static_cast(n); +} + + +pqxx::oid pqxx::result::column_table(row::size_type col_num) const +{ + oid const t{PQftable(m_data.get(), col_num)}; + + /* If we get oid_none, it may be because the column is computed, or because + * we got an invalid row number. + */ + if (t == oid_none and col_num >= columns()) + throw argument_error{internal::concat( + "Attempt to retrieve table ID for column ", col_num, " out of ", + columns())}; + + return t; +} + + +pqxx::row::size_type pqxx::result::table_column(row::size_type col_num) const +{ + auto const n{row::size_type(PQftablecol(m_data.get(), col_num))}; + if (n != 0) + PQXX_LIKELY + return n - 1; + + // Failed. Now find out why, so we can throw a sensible exception. + auto const col_str{to_string(col_num)}; + if (col_num > columns()) + throw range_error{ + internal::concat("Invalid column index in table_column(): ", col_str)}; + + if (m_data.get() == nullptr) + throw usage_error{internal::concat( + "Can't query origin of column ", col_str, + ": result is not initialized.")}; + + throw usage_error{internal::concat( + "Can't query origin of column ", col_str, + ": not derived from table column.")}; +} + + +int pqxx::result::errorposition() const +{ + int pos{-1}; + if (m_data.get()) + { + auto const p{PQresultErrorField( + const_cast(m_data.get()), + PG_DIAG_STATEMENT_POSITION)}; + if (p) + pos = from_string(p); + } + return pos; +} + + +char const *pqxx::result::column_name(pqxx::row::size_type number) const & +{ + auto const n{PQfname(m_data.get(), number)}; + if (n == nullptr) + { + PQXX_UNLIKELY + if (m_data.get() == nullptr) + throw usage_error{"Queried column name on null result."}; + throw range_error{internal::concat( + "Invalid column number: ", number, " (maximum is ", (columns() - 1), + ").")}; + } + return n; +} + + +pqxx::row::size_type pqxx::result::columns() const noexcept +{ + auto ptr{const_cast(m_data.get())}; + return (ptr == nullptr) ? 0 : row::size_type(PQnfields(ptr)); +} + + +// const_result_iterator + +pqxx::const_result_iterator pqxx::const_result_iterator::operator++(int) +{ + const_result_iterator old{*this}; + m_index++; + return old; +} + + +pqxx::const_result_iterator pqxx::const_result_iterator::operator--(int) +{ + const_result_iterator old{*this}; + m_index--; + return old; +} + + +pqxx::result::const_iterator +pqxx::result::const_reverse_iterator::base() const noexcept +{ + iterator_type tmp{*this}; + return ++tmp; +} + + +pqxx::const_reverse_result_iterator +pqxx::const_reverse_result_iterator::operator++(int) +{ + const_reverse_result_iterator tmp{*this}; + iterator_type::operator--(); + return tmp; +} + + +pqxx::const_reverse_result_iterator +pqxx::const_reverse_result_iterator::operator--(int) +{ + const_reverse_result_iterator tmp{*this}; + iterator_type::operator++(); + return tmp; +} + + +template<> std::string pqxx::to_string(field const &value) +{ + return {value.c_str(), std::size(value)}; +} diff --git a/ext/libpqxx-7.7.3/src/robusttransaction.cxx b/ext/libpqxx-7.7.3/src/robusttransaction.cxx new file mode 100644 index 000000000..5bbfd64f8 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/robusttransaction.cxx @@ -0,0 +1,221 @@ +/** Implementation of the pqxx::robusttransaction class. + * + * pqxx::robusttransaction is a slower but safer transaction class. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include +#include +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/connection.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/wait.hxx" +#include "pqxx/nontransaction.hxx" +#include "pqxx/result.hxx" +#include "pqxx/robusttransaction.hxx" + +#include "pqxx/internal/header-post.hxx" + + +using namespace std::literals; + +namespace +{ +using pqxx::operator"" _zv; + +/// Statuses in which we may find our transaction. +/** There's also "in the future," but it manifests as an error, not as an + * actual status. + */ +enum tx_stat +{ + tx_unknown, + tx_committed, + tx_aborted, + tx_in_progress, +}; + + +constexpr auto committed{"committed"_zv}, aborted{"aborted"_zv}, + in_progress{"in progress"_zv}; + + +/// Parse a nonempty transaction status string. +constexpr tx_stat parse_status(std::string_view text) noexcept +{ + switch (text[0]) + { + case 'a': + if (text == aborted) + PQXX_LIKELY return tx_aborted; + break; + case 'c': + if (text == committed) + PQXX_LIKELY return tx_committed; + break; + case 'i': + if (text == in_progress) + PQXX_LIKELY return tx_in_progress; + break; + } + return tx_unknown; +} + + +tx_stat query_status(std::string const &xid, std::string const &conn_str) +{ + static std::string const name{"robusttxck"sv}; + auto const query{pqxx::internal::concat("SELECT txid_status(", xid, ")")}; + pqxx::connection c{conn_str}; + pqxx::nontransaction w{c, name}; + auto const status_row{w.exec1(query)}; + auto const status_field{status_row[0]}; + if (std::size(status_field) == 0) + throw pqxx::internal_error{"Transaction status string is empty."}; + auto const status{parse_status(status_field.as())}; + if (status == tx_unknown) + throw pqxx::internal_error{pqxx::internal::concat( + "Unknown transaction status string: ", status_field.view())}; + return status; +} +} // namespace + + +void pqxx::internal::basic_robusttransaction::init(zview begin_command) +{ + static auto const txid_q{ + std::make_shared("SELECT txid_current()"sv)}; + m_backendpid = conn().backendpid(); + direct_exec(begin_command); + direct_exec(txid_q)[0][0].to(m_xid); +} + + +pqxx::internal::basic_robusttransaction::basic_robusttransaction( + connection &c, zview begin_command, std::string_view tname) : + dbtransaction(c, tname), m_conn_string{c.connection_string()} +{ + init(begin_command); +} + + +pqxx::internal::basic_robusttransaction::basic_robusttransaction( + connection &c, zview begin_command) : + dbtransaction(c), m_conn_string{c.connection_string()} +{ + init(begin_command); +} + + +pqxx::internal::basic_robusttransaction::~basic_robusttransaction() = default; + + +void pqxx::internal::basic_robusttransaction::do_commit() +{ + static auto const check_constraints_q{ + std::make_shared("SET CONSTRAINTS ALL IMMEDIATE"sv)}, + commit_q{std::make_shared("COMMIT"sv)}; + // Check constraints before sending the COMMIT to the database, so as to + // minimise our in-doubt window. + try + { + direct_exec(check_constraints_q); + } + catch (std::exception const &) + { + do_abort(); + throw; + } + + // Here comes the in-doubt window. If we lose our connection here, we'll be + // left clueless as to what happened on the backend. It may have received + // the commit command and completed the transaction, and ended up with a + // success it could not report back to us. Or it may have noticed the broken + // connection and aborted the transaction. It may even still be executing + // the commit, only to fail later. + // + // All this uncertainty requires some special handling, and that s what makes + // robusttransaction what it is. + try + { + direct_exec(commit_q); + + // If we make it here, great. Normal, successful commit. + return; + } + catch (broken_connection const &) + { + // Oops, lost connection at the crucial moment. Fall through to in-doubt + // handling below. + } + catch (std::exception const &) + { + if (conn().is_open()) + { + // Commit failed, for some other reason. + do_abort(); + throw; + } + // Otherwise, fall through to in-doubt handling. + } + + // If we get here, we're in doubt. Figure out what happened. + + int const max_attempts{500}; + static_assert(max_attempts > 0); + + tx_stat stat; + for (int attempts{0}; attempts < max_attempts; + ++attempts, pqxx::internal::wait_for(300u)) + { + stat = tx_unknown; + try + { + stat = query_status(m_xid, m_conn_string); + } + catch (pqxx::broken_connection const &) + { + // Swallow the error. Pause and retry. + } + switch (stat) + { + case tx_unknown: + // We were unable to reconnect and query transaction status. + // Stay in it for another attempt. + return; + case tx_committed: + // Success! We're done. + return; + case tx_aborted: + // Aborted. We're done. + do_abort(); + return; + case tx_in_progress: + // The transaction is still running. Stick around until we know what + // transpires. + break; + } + } + + // Okay, this has taken too long. Give up, report in-doubt state. + throw in_doubt_error{internal::concat( + "Transaction ", name(), " (with transaction ID ", m_xid, + ") " + "lost connection while committing. It's impossible to tell whether " + "it committed, or aborted, or is still running. " + "Attempts to find out its outcome have failed. " + "The backend process on the server had process ID ", + m_backendpid, + ". " + "You may be able to check what happened to that process.")}; +} diff --git a/ext/libpqxx-7.7.3/src/row.cxx b/ext/libpqxx-7.7.3/src/row.cxx new file mode 100644 index 000000000..f0974c839 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/row.cxx @@ -0,0 +1,250 @@ +/** Implementation of the pqxx::result class and support classes. + * + * pqxx::result represents the set of result rows from a database query. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include + +extern "C" +{ +#include +} + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/except.hxx" +#include "pqxx/result.hxx" +#include "pqxx/row.hxx" + +#include "pqxx/internal/header-post.hxx" + + +pqxx::row::row( + result const &r, result::size_type index, size_type cols) noexcept : + m_result{r}, m_index{index}, m_end{cols} +{} + + +pqxx::row::const_iterator pqxx::row::begin() const noexcept +{ + return {*this, m_begin}; +} + + +pqxx::row::const_iterator pqxx::row::cbegin() const noexcept +{ + return begin(); +} + + +pqxx::row::const_iterator pqxx::row::end() const noexcept +{ + return {*this, m_end}; +} + + +pqxx::row::const_iterator pqxx::row::cend() const noexcept +{ + return end(); +} + + +pqxx::row::reference pqxx::row::front() const noexcept +{ + return field{m_result, m_index, m_begin}; +} + + +pqxx::row::reference pqxx::row::back() const noexcept +{ + return field{m_result, m_index, m_end - 1}; +} + + +pqxx::row::const_reverse_iterator pqxx::row::rbegin() const +{ + return const_reverse_row_iterator{end()}; +} + + +pqxx::row::const_reverse_iterator pqxx::row::crbegin() const +{ + return rbegin(); +} + + +pqxx::row::const_reverse_iterator pqxx::row::rend() const +{ + return const_reverse_row_iterator{begin()}; +} + + +pqxx::row::const_reverse_iterator pqxx::row::crend() const +{ + return rend(); +} + + +bool pqxx::row::operator==(row const &rhs) const noexcept +{ + if (&rhs == this) + return true; + auto const s{size()}; + if (std::size(rhs) != s) + return false; + for (size_type i{0}; i < s; ++i) + if ((*this)[i] != rhs[i]) + return false; + return true; +} + + +pqxx::row::reference pqxx::row::operator[](size_type i) const noexcept +{ + return field{m_result, m_index, m_begin + i}; +} + + +pqxx::row::reference pqxx::row::operator[](zview col_name) const +{ + return at(col_name); +} + + +void pqxx::row::swap(row &rhs) noexcept +{ + auto const i{m_index}; + auto const b{m_begin}; + auto const e{m_end}; + m_result.swap(rhs.m_result); + m_index = rhs.m_index; + m_begin = rhs.m_begin; + m_end = rhs.m_end; + rhs.m_index = i; + rhs.m_begin = b; + rhs.m_end = e; +} + + +pqxx::field pqxx::row::at(zview col_name) const +{ + return {m_result, m_index, m_begin + column_number(col_name)}; +} + + +pqxx::field pqxx::row::at(pqxx::row::size_type i) const +{ + if (i >= size()) + throw range_error{"Invalid field number."}; + + return operator[](i); +} + + +pqxx::oid pqxx::row::column_type(size_type col_num) const +{ + return m_result.column_type(m_begin + col_num); +} + + +pqxx::oid pqxx::row::column_table(size_type col_num) const +{ + return m_result.column_table(m_begin + col_num); +} + + +pqxx::row::size_type pqxx::row::table_column(size_type col_num) const +{ + return m_result.table_column(m_begin + col_num); +} + + +pqxx::row::size_type pqxx::row::column_number(zview col_name) const +{ + auto const n{m_result.column_number(col_name)}; + if (n >= m_end) + throw argument_error{ + "Column '" + std::string{col_name} + "' falls outside slice."}; + if (n >= m_begin) + return n - m_begin; + + // This deals with a really nasty possibility: that the column name occurs + // twice - once before the beginning of the slice, and once inside the slice. + char const *const adapted_name{m_result.column_name(n)}; + for (auto i{m_begin}; i < m_end; ++i) + if (strcmp(adapted_name, m_result.column_name(i)) == 0) + return i - m_begin; + + // Didn't find any? Recurse just to produce the same error message. + return result{}.column_number(col_name); +} + + +pqxx::row PQXX_COLD pqxx::row::slice(size_type sbegin, size_type send) const +{ + if (sbegin > send or send > size()) + throw range_error{"Invalid field range."}; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + row res{*this}; +#include "pqxx/internal/ignore-deprecated-post.hxx" + res.m_begin = m_begin + sbegin; + res.m_end = m_begin + send; + return res; +} + + +bool PQXX_COLD pqxx::row::empty() const noexcept +{ + return m_begin == m_end; +} + + +pqxx::const_row_iterator pqxx::const_row_iterator::operator++(int) +{ + auto const old{*this}; + m_col++; + return old; +} + + +pqxx::const_row_iterator pqxx::const_row_iterator::operator--(int) +{ + auto const old{*this}; + m_col--; + return old; +} + + +pqxx::const_row_iterator +pqxx::const_reverse_row_iterator::base() const noexcept +{ + iterator_type tmp{*this}; + return ++tmp; +} + + +pqxx::const_reverse_row_iterator +pqxx::const_reverse_row_iterator::operator++(int) +{ + auto tmp{*this}; + operator++(); + return tmp; +} + + +pqxx::const_reverse_row_iterator +pqxx::const_reverse_row_iterator::operator--(int) +{ + auto tmp{*this}; + operator--(); + return tmp; +} diff --git a/ext/libpqxx-7.7.3/src/sql_cursor.cxx b/ext/libpqxx-7.7.3/src/sql_cursor.cxx new file mode 100644 index 000000000..1e5d6a790 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/sql_cursor.cxx @@ -0,0 +1,276 @@ +/** Implementation of libpqxx STL-style cursor classes. + * + * These classes wrap SQL cursors in STL-like interfaces. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/cursor.hxx" +#include "pqxx/internal/encodings.hxx" +#include "pqxx/internal/gates/connection-sql_cursor.hxx" +#include "pqxx/internal/gates/transaction-sql_cursor.hxx" + +#include "pqxx/internal/header-post.hxx" + + +using namespace std::literals; + +namespace +{ +/// Is this character a "useless trailing character" in a query? +/** A character is "useless" at the end of a query if it is either whitespace + * or a semicolon. + */ +inline bool useless_trail(char c) +{ + return isspace(c) or c == ';'; +} + + +/// Find end of nonempty query, stripping off any trailing semicolon. +/** When executing a normal query, a trailing semicolon is meaningless but + * won't hurt. That's why we can't rule out that some code may include one. + * + * But for cursor queries, a trailing semicolon is a problem. The query gets + * embedded in a larger statement, which a semicolon would break into two. + * We'll have to remove it if present. + * + * A trailing semicolon may not actually be at the end. It could be masked by + * subsequent whitespace. If there's also a comment though, that's the + * caller's own lookout. We can't guard against every possible mistake, and + * text processing is actually remarkably sensitive to mistakes in a + * multi-encoding world. + * + * If there is a trailing semicolon, this function returns its offset. If + * there are more than one, it returns the offset of the first one. If there + * is no trailing semicolon, it returns the length of the query string. + * + * The query must be nonempty. + */ +std::string::size_type +find_query_end(std::string_view query, pqxx::internal::encoding_group enc) +{ + auto const text{std::data(query)}; + auto const size{std::size(query)}; + std::string::size_type end; + if (enc == pqxx::internal::encoding_group::MONOBYTE) + { + // This is an encoding where we can scan backwards from the end. + for (end = std::size(query); end > 0 and useless_trail(text[end - 1]); + --end) + ; + } + else + { + // Complex encoding. We only know how to iterate forwards, so start from + // the beginning. + end = 0; + + pqxx::internal::for_glyphs( + enc, + [text, &end](char const *gbegin, char const *gend) { + if (gend - gbegin > 1 or not useless_trail(*gbegin)) + end = std::string::size_type(gend - text); + }, + text, size); + } + + return end; +} +} // namespace + + +pqxx::internal::sql_cursor::sql_cursor( + transaction_base &t, std::string_view query, std::string_view cname, + cursor_base::access_policy ap, cursor_base::update_policy up, + cursor_base::ownership_policy op, bool hold) : + cursor_base{t.conn(), cname}, + m_home{t.conn()}, + m_adopted{false}, + m_at_end{-1}, + m_pos{0} +{ + if (&t.conn() != &m_home) + throw internal_error{"Cursor in wrong connection"}; + + if (std::empty(query)) + throw usage_error{"Cursor has empty query."}; + auto const enc{enc_group(t.conn().encoding_id())}; + auto const qend{find_query_end(query, enc)}; + if (qend == 0) + throw usage_error{"Cursor has effectively empty query."}; + query.remove_suffix(std::size(query) - qend); + + std::string const cq{internal::concat( + "DECLARE "sv, t.quote_name(name()), " "sv, + ((ap == cursor_base::forward_only) ? "NO "sv : ""sv), "SCROLL CURSOR "sv, + (hold ? "WITH HOLD "sv : ""sv), "FOR "sv, query, " "sv, + ((up == cursor_base::update) ? "FOR UPDATE "sv : "FOR READ ONLY "sv))}; + + t.exec(cq); + + // Now that we're here in the starting position, keep a copy of an empty + // result. That may come in handy later, because we may not be able to + // construct an empty result with all the right metadata due to the weird + // meaning of "FETCH 0." + init_empty_result(t); + + m_ownership = op; +} + + +pqxx::internal::sql_cursor::sql_cursor( + transaction_base &t, std::string_view cname, + cursor_base::ownership_policy op) : + cursor_base{t.conn(), cname, false}, + m_home{t.conn()}, + m_empty_result{}, + m_adopted{true}, + m_at_end{0}, + m_pos{-1} +{ + m_adopted = true; + m_ownership = op; +} + + +void pqxx::internal::sql_cursor::close() noexcept +{ + if (m_ownership == cursor_base::owned) + { + try + { + gate::connection_sql_cursor{m_home}.exec( + internal::concat("CLOSE "sv, m_home.quote_name(name())).c_str()); + } + catch (std::exception const &) + {} + m_ownership = cursor_base::loose; + } +} + + +void pqxx::internal::sql_cursor::init_empty_result(transaction_base &t) +{ + if (pos() != 0) + throw internal_error{"init_empty_result() from bad pos()."}; + m_empty_result = + t.exec(internal::concat("FETCH 0 IN "sv, m_home.quote_name(name()))); +} + + +/// Compute actual displacement based on requested and reported displacements. +pqxx::internal::sql_cursor::difference_type pqxx::internal::sql_cursor::adjust( + difference_type hoped, difference_type actual) +{ + if (actual < 0) + throw internal_error{"Negative rows in cursor movement."}; + if (hoped == 0) + return 0; + int const direction{((hoped < 0) ? -1 : 1)}; + bool hit_end{false}; + if (actual != labs(hoped)) + { + if (actual > labs(hoped)) + throw internal_error{"Cursor displacement larger than requested."}; + + // If we see fewer rows than requested, then we've hit an end (on either + // side) of the result set. Wether we make an extra step to a one-past-end + // position or whether we're already there depends on where we were + // previously: if our last move was in the same direction and also fell + // short, we're already at a one-past-end row. + if (m_at_end != direction) + ++actual; + + // If we hit the beginning, make sure our position calculation ends up + // at zero (even if we didn't previously know where we were!), and if we + // hit the other end, register the fact that we now know where the end + // of the result set is. + if (direction > 0) + hit_end = true; + else if (m_pos == -1) + m_pos = actual; + else if (m_pos != actual) + throw internal_error{internal::concat( + "Moved back to beginning, but wrong position: hoped=", hoped, + ", actual=", actual, ", m_pos=", m_pos, ", direction=", direction, + ".")}; + + m_at_end = direction; + } + else + { + m_at_end = 0; + } + + if (m_pos >= 0) + m_pos += direction * actual; + if (hit_end) + { + if (m_endpos >= 0 and m_pos != m_endpos) + throw internal_error{"Inconsistent cursor end positions."}; + m_endpos = m_pos; + } + return direction * actual; +} + + +pqxx::result pqxx::internal::sql_cursor::fetch( + difference_type rows, difference_type &displacement) +{ + if (rows == 0) + { + displacement = 0; + return m_empty_result; + } + auto const query{pqxx::internal::concat( + "FETCH "sv, stridestring(rows), " IN "sv, m_home.quote_name(name()))}; + auto const r{gate::connection_sql_cursor{m_home}.exec(query.c_str())}; + displacement = adjust(rows, difference_type(std::size(r))); + return r; +} + + +pqxx::cursor_base::difference_type pqxx::internal::sql_cursor::move( + difference_type rows, difference_type &displacement) +{ + if (rows == 0) + { + displacement = 0; + return 0; + } + + auto const query{pqxx::internal::concat( + "MOVE "sv, stridestring(rows), " IN "sv, m_home.quote_name(name()))}; + auto const r{gate::connection_sql_cursor{m_home}.exec(query.c_str())}; + auto d{static_cast(r.affected_rows())}; + displacement = adjust(rows, d); + return d; +} + + +std::string pqxx::internal::sql_cursor::stridestring(difference_type n) +{ + /* Some special-casing for ALL and BACKWARD ALL here. We used to use numeric + * "infinities" for difference_type for this (the highest and lowest possible + * values for "long"), but for PostgreSQL 8.0 at least, the backend appears + * to expect a 32-bit number and fails to parse large 64-bit numbers. We + * could change the alias to match this behaviour, but that would break + * if/when Postgres is changed to accept 64-bit displacements. + */ + static std::string const All{"ALL"}, BackAll{"BACKWARD ALL"}; + if (n >= cursor_base::all()) + return All; + else if (n <= cursor_base::backward_all()) + return BackAll; + return to_string(n); +} diff --git a/ext/libpqxx-7.7.3/src/strconv.cxx b/ext/libpqxx-7.7.3/src/strconv.cxx new file mode 100644 index 000000000..2bd206b2a --- /dev/null +++ b/ext/libpqxx-7.7.3/src/strconv.cxx @@ -0,0 +1,785 @@ +/** Implementation of string conversions. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __has_include() +# include +#endif + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/except.hxx" +#include "pqxx/strconv.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace +{ +#if !defined(PQXX_HAVE_CHARCONV_FLOAT) +/// Do we have fully functional thread_local support? +/** When building with libcxxrt on clang, you can't create thread_local objects + * of non-POD types. Any attempt will result in a link error. + */ +constexpr bool have_thread_local +{ +# if defined(PQXX_HAVE_THREAD_LOCAL) + true +# else + false +# endif +}; +#endif + + +/// String comparison between string_view. +constexpr inline bool equal(std::string_view lhs, std::string_view rhs) +{ + return lhs.compare(rhs) == 0; +} + + +/// The lowest possible value of integral type T. +template constexpr T bottom{std::numeric_limits::min()}; + +/// The highest possible value of integral type T. +template constexpr T top{std::numeric_limits::max()}; + +/// Write nonnegative integral value at end of buffer. Return start. +/** Assumes a sufficiently large buffer. + * + * Includes a single trailing null byte, right before @c *end. + */ +template constexpr inline char *nonneg_to_buf(char *end, T value) +{ + char *pos = end; + *--pos = '\0'; + do { + *--pos = pqxx::internal::number_to_digit(int(value % 10)); + value = T(value / 10); + } while (value > 0); + return pos; +} + + +/// Write negative version of value at end of buffer. Return start. +/** Like @c nonneg_to_buf, but prefixes a minus sign. + */ +template constexpr inline char *neg_to_buf(char *end, T value) +{ + char *pos = nonneg_to_buf(end, value); + *--pos = '-'; + return pos; +} + + +/// Write lowest possible negative value at end of buffer. +/** Like @c neg_to_buf, but for the special case of the bottom value. + */ +template constexpr inline char *bottom_to_buf(char *end) +{ + static_assert(std::is_signed_v); + + // This is the hard case. In two's-complement systems, which includes + // any modern-day system I can think of, a signed type's bottom value + // has no positive equivalent. Luckily the C++ standards committee can't + // think of any exceptions either, so it's the required representation as + // of C++20. We'll assume it right now, while still on C++17. + static_assert(-(bottom + 1) == top); + + // The unsigned version of T does have the unsigned version of bottom. + using unsigned_t = std::make_unsigned_t; + + // Careful though. If we tried to negate value in order to promote to + // unsigned_t, the value will overflow, which means behaviour is + // undefined. Promotion of a negative value to an unsigned type is + // well-defined, given a representation, so let's do that: + constexpr auto positive{static_cast(bottom)}; + + // As luck would have it, in two's complement, this gives us exactly the + // value we want. + static_assert(positive == top / 2 + 1); + + // So the only thing we need to do differently from the regular negative + // case is to skip that overflowing negation and promote to an unsigned type! + return neg_to_buf(end, positive); +} + + +#if defined(PQXX_HAVE_CHARCONV_INT) || defined(PQXX_HAVE_CHARCONV_FLOAT) +/// Call to_chars, report errors as exceptions, add zero, return pointer. +template +[[maybe_unused]] inline char * +wrap_to_chars(char *begin, char *end, T const &value) +{ + auto res{std::to_chars(begin, end - 1, value)}; + if (res.ec != std::errc()) + PQXX_UNLIKELY + switch (res.ec) + { + case std::errc::value_too_large: + throw pqxx::conversion_overrun{ + "Could not convert " + pqxx::type_name + + " to string: " + "buffer too small (" + + pqxx::to_string(end - begin) + " bytes)."}; + default: + throw pqxx::conversion_error{ + "Could not convert " + pqxx::type_name + " to string."}; + } + // No need to check for overrun here: we never even told to_chars about that + // last byte in the buffer, so it didn't get used up. + *res.ptr++ = '\0'; + return res.ptr; +} +#endif +} // namespace + + +namespace pqxx::internal +{ +template +zview integral_traits::to_buf(char *begin, char *end, T const &value) +{ + static_assert(std::is_integral_v); + auto const space{end - begin}, + need{static_cast(size_buffer(value))}; + if (space < need) + throw conversion_overrun{ + "Could not convert " + type_name + + " to string: " + "buffer too small. " + + pqxx::internal::state_buffer_overrun(space, need)}; + + char *pos; + if constexpr (std::is_unsigned_v) + pos = nonneg_to_buf(end, value); + else if (value >= 0) + pos = nonneg_to_buf(end, value); + else if (value > bottom) + pos = neg_to_buf(end, -value); + else + pos = bottom_to_buf(end); + + return {pos, end - pos - 1}; +} + + +template zview integral_traits::to_buf(char *, char *, short const &); +template zview integral_traits::to_buf( + char *, char *, unsigned short const &); +template zview integral_traits::to_buf(char *, char *, int const &); +template zview +integral_traits::to_buf(char *, char *, unsigned const &); +template zview integral_traits::to_buf(char *, char *, long const &); +template zview +integral_traits::to_buf(char *, char *, unsigned long const &); +template zview +integral_traits::to_buf(char *, char *, long long const &); +template zview integral_traits::to_buf( + char *, char *, unsigned long long const &); + + +template +char *integral_traits::into_buf(char *begin, char *end, T const &value) +{ +#if defined(PQXX_HAVE_CHARCONV_INT) + // This is exactly what to_chars is good at. Trust standard library + // implementers to optimise better than we can. + return wrap_to_chars(begin, end, value); +#else + return generic_into_buf(begin, end, value); +#endif +} + + +template char *integral_traits::into_buf(char *, char *, short const &); +template char *integral_traits::into_buf( + char *, char *, unsigned short const &); +template char *integral_traits::into_buf(char *, char *, int const &); +template char * +integral_traits::into_buf(char *, char *, unsigned const &); +template char *integral_traits::into_buf(char *, char *, long const &); +template char *integral_traits::into_buf( + char *, char *, unsigned long const &); +template char * +integral_traits::into_buf(char *, char *, long long const &); +template char *integral_traits::into_buf( + char *, char *, unsigned long long const &); +} // namespace pqxx::internal + + +namespace pqxx::internal +{ +std::string demangle_type_name(char const raw[]) +{ +#if defined(PQXX_HAVE_CXA_DEMANGLE) + // We've got __cxa_demangle. Use it to get a friendlier type name. + int status{0}; + + // We've seen this fail on FreeBSD 11.3 (see #361). Trying to throw a + // meaningful exception only made things worse. So in case of error, just + // fall back to the raw name. + // + // When __cxa_demangle fails, it's guaranteed to return null. + char *demangled{abi::__cxa_demangle(raw, nullptr, nullptr, &status)}; +#else + static constexpr char *demangled{nullptr}; +#endif + std::string const name{(demangled == nullptr) ? raw : demangled}; + + // Check for nullness to work around jemalloc bug (see #508). + if (demangled != nullptr) + std::free(demangled); + return name; +} + +void PQXX_COLD throw_null_conversion(std::string const &type) +{ + throw conversion_error{"Attempt to convert null to " + type + "."}; +} + + +std::string PQXX_COLD state_buffer_overrun(int have_bytes, int need_bytes) +{ + // We convert these in standard library terms, not for the localisation + // so much as to avoid "error cycles," if these values in turn should fail + // to get enough buffer space. + std::stringstream have, need; + have << have_bytes; + need << need_bytes; + return "Have " + have.str() + " bytes, need " + need.str() + "."; +} +} // namespace pqxx::internal + + +namespace +{ +#if defined(PQXX_HAVE_CHARCONV_INT) || defined(PQXX_HAVE_CHARCONV_FLOAT) +template +[[maybe_unused]] inline TYPE from_string_arithmetic(std::string_view in) +{ + char const *here; + auto const end{std::data(in) + std::size(in)}; + + // Skip whitespace. This is not the proper way to do it, but I see no way + // that any of the supported encodings could ever produce a valid character + // whose byte sequence would confuse this code. + for (here = std::data(in); here < end and (*here == ' ' or *here == '\t'); + ++here) + ; + + TYPE out{}; + auto const res{std::from_chars(here, end, out)}; + if (res.ec == std::errc() and res.ptr == end) + PQXX_LIKELY + return out; + + std::string msg; + if (res.ec == std::errc()) + { + msg = "Could not parse full string."; + } + else + { + switch (res.ec) + { + case std::errc::result_out_of_range: msg = "Value out of range."; break; + case std::errc::invalid_argument: msg = "Invalid argument."; break; + default: break; + } + } + + auto const base{ + "Could not convert '" + std::string(in) + + "' " + "to " + + pqxx::type_name}; + if (std::empty(msg)) + throw pqxx::conversion_error{base + "."}; + else + throw pqxx::conversion_error{base + ": " + msg}; +} +#endif +} // namespace + + +namespace +{ +#if !defined(PQXX_HAVE_CHARCONV_INT) +[[noreturn, maybe_unused]] void PQXX_COLD report_overflow() +{ + throw pqxx::conversion_error{ + "Could not convert string to integer: value out of range."}; +} + +template struct numeric_ten +{ + static inline constexpr T value = 10; +}; + +template struct numeric_high_threshold +{ + static inline constexpr T value = + (std::numeric_limits::max)() / numeric_ten::value; +}; + +template struct numeric_low_threshold +{ + static inline constexpr T value = + (std::numeric_limits::min)() / numeric_ten::value; +}; + +/// Return 10*n, or throw exception if it overflows. +template +[[maybe_unused]] constexpr inline T safe_multiply_by_ten(T n) +{ + using limits = std::numeric_limits; + + if (n > numeric_high_threshold::value) + PQXX_UNLIKELY + report_overflow(); + if constexpr (limits::is_signed) + { + if (numeric_low_threshold::value > n) + PQXX_UNLIKELY + report_overflow(); + } + return T(n * numeric_ten::value); +} + + +/// Add digit d to nonnegative n, or throw exception if it overflows. +template +[[maybe_unused]] constexpr inline T safe_add_digit(T n, T d) +{ + T const high_threshold{static_cast(std::numeric_limits::max() - d)}; + if (n > high_threshold) + PQXX_UNLIKELY + report_overflow(); + return static_cast(n + d); +} + + +/// Subtract digit d to nonpositive n, or throw exception if it overflows. +template +[[maybe_unused]] constexpr inline T safe_sub_digit(T n, T d) +{ + T const low_threshold{static_cast(std::numeric_limits::min() + d)}; + if (n < low_threshold) + PQXX_UNLIKELY + report_overflow(); + return static_cast(n - d); +} + + +/// For use in string parsing: add new numeric digit to intermediate value. +template +[[maybe_unused]] constexpr inline L absorb_digit_positive(L value, R digit) +{ + return safe_add_digit(safe_multiply_by_ten(value), L(digit)); +} + + +/// For use in string parsing: subtract digit from intermediate value. +template +[[maybe_unused]] constexpr inline L absorb_digit_negative(L value, R digit) +{ + return safe_sub_digit(safe_multiply_by_ten(value), L(digit)); +} + + +template +[[maybe_unused]] constexpr T from_string_integer(std::string_view text) +{ + if (std::size(text) == 0) + throw pqxx::conversion_error{ + "Attempt to convert empty string to " + pqxx::type_name + "."}; + + char const *const data{std::data(text)}; + std::size_t i{0}; + + // Skip whitespace. This is not the proper way to do it, but I see no way + // that any of the supported encodings could ever produce a valid character + // whose byte sequence would confuse this code. + // + // Why skip whitespace? Because that's how integral conversions are meant to + // work _for composite types._ I see no clean way to support leading + // whitespace there without putting the code in here. A shame about the + // overhead, modest as it is, for the normal case. + for (; i < std::size(text) and (data[i] == ' ' or data[i] == '\t'); ++i) + ; + if (i == std::size(text)) + throw pqxx::conversion_error{ + "Converting string to " + pqxx::type_name + + ", but it contains only whitespace."}; + + char const initial{data[i]}; + T result{0}; + + if (pqxx::internal::is_digit(initial)) + { + for (; pqxx::internal::is_digit(data[i]); ++i) + result = absorb_digit_positive( + result, pqxx::internal::digit_to_number(data[i])); + } + else if (initial == '-') + { + if constexpr (not std::is_signed_v) + throw pqxx::conversion_error{ + "Attempt to convert negative value to " + pqxx::type_name + "."}; + + ++i; + if (i >= std::size(text)) + throw pqxx::conversion_error{ + "Converting string to " + pqxx::type_name + + ", but it contains only a sign."}; + for (; i < std::size(text) and pqxx::internal::is_digit(data[i]); ++i) + result = absorb_digit_negative( + result, pqxx::internal::digit_to_number(data[i])); + } + else + { + throw pqxx::conversion_error{ + "Could not convert string to " + pqxx::type_name + + ": " + "'" + + std::string{text} + "'."}; + } + + if (i < std::size(text)) + throw pqxx::conversion_error{ + "Unexpected text after " + pqxx::type_name + + ": " + "'" + + std::string{text} + "'."}; + + return result; +} +#endif // !PQXX_HAVE_CHARCONV_INT +} // namespace + + +namespace +{ +[[maybe_unused]] constexpr bool +valid_infinity_string(std::string_view text) noexcept +{ + return equal("infinity", text) or equal("Infinity", text) or + equal("INFINITY", text) or equal("inf", text); +} +} // namespace + + +#if !defined(PQXX_HAVE_CHARCONV_FLOAT) +namespace +{ +/// Wrapper for std::stringstream with C locale. +/** We use this to work around missing std::to_chars for floating-point types. + * + * Initialising the stream (including locale and tweaked precision) seems to + * be expensive. So, create thread-local instances which we re-use. It's a + * lockless way of keeping global variables thread-safe, basically. + * + * The stream initialisation happens once per thread, in the constructor. + * And that's why we need to wrap this in a class. We can't just do it at the + * call site, or we'd still be doing it for every call. + */ +template class dumb_stringstream : public std::stringstream +{ +public: + // Do not initialise the base-class object using "stringstream{}" (with curly + // braces): that breaks on Visual C++. The classic "stringstream()" syntax + // (with parentheses) does work. + PQXX_COLD dumb_stringstream() + { + this->imbue(std::locale::classic()); + this->precision(std::numeric_limits::max_digits10); + } +}; + + +template +inline bool PQXX_COLD from_dumb_stringstream( + dumb_stringstream &s, F &result, std::string_view text) +{ + s.str(std::string{text}); + return static_cast(s >> result); +} + + +// These are hard, and some popular compilers still lack std::from_chars. +template +inline T PQXX_COLD from_string_awful_float(std::string_view text) +{ + if (std::empty(text)) + throw pqxx::conversion_error{ + "Trying to convert empty string to " + pqxx::type_name + "."}; + + bool ok{false}; + T result; + + switch (text[0]) + { + case 'N': + case 'n': + // Accept "NaN," "nan," etc. + ok = + (std::size(text) == 3 and (text[1] == 'A' or text[1] == 'a') and + (text[2] == 'N' or text[2] == 'n')); + result = std::numeric_limits::quiet_NaN(); + break; + + case 'I': + case 'i': + ok = valid_infinity_string(text); + result = std::numeric_limits::infinity(); + break; + + default: + if (text[0] == '-' and valid_infinity_string(text.substr(1))) + { + ok = true; + result = -std::numeric_limits::infinity(); + } + else + { + PQXX_LIKELY + if constexpr (have_thread_local) + { + thread_local dumb_stringstream S; + // Visual Studio 2017 seems to fail on repeated conversions if the + // clear() is done before the seekg(). Still don't know why! See #124 + // and #125. + S.seekg(0); + S.clear(); + ok = from_dumb_stringstream(S, result, text); + } + else + { + dumb_stringstream S; + ok = from_dumb_stringstream(S, result, text); + } + } + break; + } + + if (not ok) + throw pqxx::conversion_error{ + "Could not convert string to numeric value: '" + std::string{text} + + "'."}; + + return result; +} +} // namespace +#endif // !PQXX_HAVE_CHARCONV_FLOAT + + +namespace pqxx::internal +{ +/// Floating-point to_buf implemented in terms of to_string. +template +zview float_traits::to_buf(char *begin, char *end, T const &value) +{ +#if defined(PQXX_HAVE_CHARCONV_FLOAT) + { + // Definitely prefer to let the standard library handle this! + auto const ptr{wrap_to_chars(begin, end, value)}; + return zview{begin, std::size_t(ptr - begin - 1)}; + } +#else + { + // Implement it ourselves. Weird detail: since this workaround is based on + // std::stringstream, which produces a std::string, it's actually easier to + // build the to_buf() on top of the to_string() than the other way around. + if (std::isnan(value)) + return "nan"_zv; + if (std::isinf(value)) + return (value > 0) ? "infinity"_zv : "-infinity"_zv; + auto text{to_string_float(value)}; + auto have{end - begin}; + auto need{std::size(text) + 1}; + if (need > std::size_t(have)) + throw conversion_error{ + "Could not convert floating-point number to string: " + "buffer too small. " + + state_buffer_overrun(have, need)}; + text.copy(begin, need); + return zview{begin, std::size(text)}; + } +#endif +} + + +template zview float_traits::to_buf(char *, char *, float const &); +template zview float_traits::to_buf(char *, char *, double const &); +template zview +float_traits::to_buf(char *, char *, long double const &); + + +template +char *float_traits::into_buf(char *begin, char *end, T const &value) +{ +#if defined(PQXX_HAVE_CHARCONV_FLOAT) + return wrap_to_chars(begin, end, value); +#else + return generic_into_buf(begin, end, value); +#endif +} + + +template char *float_traits::into_buf(char *, char *, float const &); +template char *float_traits::into_buf(char *, char *, double const &); +template char * +float_traits::into_buf(char *, char *, long double const &); + + +#if !defined(PQXX_HAVE_CHARCONV_FLOAT) +template +inline std::string PQXX_COLD +to_dumb_stringstream(dumb_stringstream &s, F value) +{ + s.str(""); + s << value; + return s.str(); +} +#endif + + +/// Floating-point implementations for @c pqxx::to_string(). +template std::string to_string_float(T value) +{ +#if defined(PQXX_HAVE_CHARCONV_FLOAT) + { + static constexpr auto space{float_traits::size_buffer(value)}; + std::string buf; + buf.resize(space); + std::string_view const view{ + float_traits::to_buf(std::data(buf), std::data(buf) + space, value)}; + buf.resize(static_cast(std::end(view) - std::begin(view))); + return buf; + } +#else + { + // In this rare case, we can convert to std::string but not to a simple + // buffer. So, implement to_buf in terms of to_string instead of the other + // way around. + if constexpr (have_thread_local) + { + thread_local dumb_stringstream s; + return to_dumb_stringstream(s, value); + } + else + { + dumb_stringstream s; + return to_dumb_stringstream(s, value); + } + } +#endif +} +} // namespace pqxx::internal + + +namespace pqxx::internal +{ +template T integral_traits::from_string(std::string_view text) +{ +#if defined(PQXX_HAVE_CHARCONV_INT) + return from_string_arithmetic(text); +#else + return from_string_integer(text); +#endif +} + +template short integral_traits::from_string(std::string_view); +template unsigned short + integral_traits::from_string(std::string_view); +template int integral_traits::from_string(std::string_view); +template unsigned integral_traits::from_string(std::string_view); +template long integral_traits::from_string(std::string_view); +template unsigned long + integral_traits::from_string(std::string_view); +template long long integral_traits::from_string(std::string_view); +template unsigned long long + integral_traits::from_string(std::string_view); + + +template T float_traits::from_string(std::string_view text) +{ +#if defined(PQXX_HAVE_CHARCONV_FLOAT) + return from_string_arithmetic(text); +#else + return from_string_awful_float(text); +#endif +} + + +template float float_traits::from_string(std::string_view); +template double float_traits::from_string(std::string_view); +template long double float_traits::from_string(std::string_view); + + +template std::string to_string_float(float); +template std::string to_string_float(double); +template std::string to_string_float(long double); +} // namespace pqxx::internal + + +bool pqxx::string_traits::from_string(std::string_view text) +{ + std::optional result; + + switch (std::size(text)) + { + case 0: result = false; break; + + case 1: + switch (text[0]) + { + case 'f': + case 'F': + case '0': result = false; break; + + case 't': + case 'T': + case '1': result = true; break; + + default: break; + } + break; + + case 4: + if (equal(text, "true") or equal(text, "TRUE")) + result = true; + break; + + case 5: + if (equal(text, "false") or equal(text, "FALSE")) + result = false; + break; + + default: break; + } + + if (result) + return *result; + else + throw conversion_error{ + "Failed conversion to bool: '" + std::string{text} + "'."}; +} diff --git a/ext/libpqxx-7.7.3/src/stream_from.cxx b/ext/libpqxx-7.7.3/src/stream_from.cxx new file mode 100644 index 000000000..f710300ea --- /dev/null +++ b/ext/libpqxx-7.7.3/src/stream_from.cxx @@ -0,0 +1,327 @@ +/** Implementation of the pqxx::stream_from class. + * + * pqxx::stream_from enables optimized batch reads from a database table. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/internal/encodings.hxx" +#include "pqxx/internal/gates/connection-stream_from.hxx" +#include "pqxx/stream_from.hxx" +#include "pqxx/transaction_base.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace +{ +pqxx::internal::glyph_scanner_func * +get_scanner(pqxx::transaction_base const &tx) +{ + auto const group{pqxx::internal::enc_group(tx.conn().encoding_id())}; + return pqxx::internal::get_glyph_scanner(group); +} + + +constexpr std::string_view class_name{"stream_from"}; +} // namespace + + +pqxx::stream_from::stream_from( + transaction_base &tx, from_query_t, std::string_view query) : + transaction_focus{tx, class_name}, m_glyph_scanner{get_scanner(tx)} +{ + tx.exec0(internal::concat("COPY ("sv, query, ") TO STDOUT"sv)); + register_me(); +} + + +pqxx::stream_from::stream_from( + transaction_base &tx, from_table_t, std::string_view table) : + transaction_focus{tx, class_name, table}, + m_glyph_scanner{get_scanner(tx)} +{ + tx.exec0(internal::concat("COPY "sv, tx.quote_name(table), " TO STDOUT"sv)); + register_me(); +} + + +pqxx::stream_from::stream_from( + transaction_base &tx, std::string_view table, std::string_view columns, + from_table_t) : + transaction_focus{tx, class_name, table}, + m_glyph_scanner{get_scanner(tx)} +{ + if (std::empty(columns)) + PQXX_UNLIKELY + tx.exec0(internal::concat("COPY "sv, table, " TO STDOUT"sv)); + else PQXX_LIKELY tx.exec0( + internal::concat("COPY "sv, table, "("sv, columns, ") TO STDOUT"sv)); + register_me(); +} + + +pqxx::stream_from::stream_from( + transaction_base &tx, std::string_view unquoted_table, + std::string_view columns, from_table_t, int) : + stream_from{ + tx, tx.conn().quote_table(unquoted_table), columns, from_table} +{} + + +pqxx::stream_from pqxx::stream_from::raw_table( + transaction_base &tx, std::string_view path, std::string_view columns) +{ + return {tx, path, columns, from_table}; +} + + +pqxx::stream_from pqxx::stream_from::table( + transaction_base &tx, table_path path, + std::initializer_list columns) +{ + auto const &conn{tx.conn()}; + return raw_table(tx, conn.quote_table(path), conn.quote_columns(columns)); +} + + +pqxx::stream_from::~stream_from() noexcept +{ + try + { + close(); + } + catch (std::exception const &e) + { + reg_pending_error(e.what()); + } +} + + +pqxx::stream_from::raw_line pqxx::stream_from::get_raw_line() +{ + if (*this) + { + internal::gate::connection_stream_from gate{m_trans.conn()}; + try + { + raw_line line{gate.read_copy_line()}; + if (line.first.get() == nullptr) + close(); + return line; + } + catch (std::exception const &) + { + close(); + throw; + } + } + else + { + return {}; + } +} + + +void pqxx::stream_from::close() +{ + if (not m_finished) + { + PQXX_UNLIKELY + m_finished = true; + unregister_me(); + } +} + + +void pqxx::stream_from::complete() +{ + if (m_finished) + return; + try + { + // Flush any remaining lines - libpq will automatically close the stream + // when it hits the end. + bool done{false}; + while (not done) + { + auto [line, size] = get_raw_line(); + ignore_unused(size); + done = not line.get(); + } + } + catch (broken_connection const &) + { + close(); + throw; + } + catch (std::exception const &e) + { + reg_pending_error(e.what()); + } + close(); +} + + +void pqxx::stream_from::parse_line() +{ + if (m_finished) + PQXX_UNLIKELY + return; + auto const next_seq{m_glyph_scanner}; + + m_fields.clear(); + + auto const [line, line_size] = get_raw_line(); + if (line.get() == nullptr) + { + m_finished = true; + return; + } + + if (line_size >= (std::numeric_limits::max() / 2)) + throw range_error{"Stream produced a ridiculously long line."}; + + // Make room for unescaping the line. It's a pessimistic size. + // Unusually, we're storing terminating zeroes *inside* the string. + // This is the only place where we modify m_row. MAKE SURE THE BUFFER DOES + // NOT GET RESIZED while we're working, because we're working with views into + // its buffer. + m_row.resize(line_size + 1); + + char const *line_begin{line.get()}; + char const *line_end{line_begin + line_size}; + char const *read{line_begin}; + + // Output iterator for unescaped text. + char *write{m_row.data()}; + + // The pointer cannot be null at this point. But we initialise field_begin + // with this value, and carry it around the loop, and it can later become + // null. Static analysis in clang-tidy then likes to assume a case where + // field_begin is null, and deduces from this that "write" must have been + // null -- and so it marks "*write" as a null pointer dereference. + // + // This assertion tells clang-tidy just what it needs in order to deduce + // that *write never dereferences a null pointer. + assert(write != nullptr); + + // Beginning of current field in m_row, or nullptr for null fields. + char const *field_begin{write}; + + while (read < line_end) + { + auto const offset{static_cast(read - line_begin)}; + auto const glyph_end{line_begin + next_seq(line_begin, line_size, offset)}; + // XXX: find_char<'\t', '\\'>(). + if (glyph_end == read + 1) + { + // Single-byte character. + char c{*read++}; + switch (c) + { + case '\t': // Field separator. + // End the field. + if (field_begin == nullptr) + { + m_fields.emplace_back(); + } + else + { + // Would love to emplace_back() here, but gcc 9.1 warns about the + // constructor not throwing. It suggests adding "noexcept." Which + // we can hardly do, without std::string_view guaranteeing it. + m_fields.push_back(zview{field_begin, write - field_begin}); + *write++ = '\0'; + } + field_begin = write; + break; + + PQXX_UNLIKELY + case '\\': { + // Escape sequence. + if (read >= line_end) + throw failure{"Row ends in backslash"}; + + c = *read++; + switch (c) + { + case 'N': + // Null value. + if (write != field_begin) + throw failure{"Null sequence found in nonempty field"}; + field_begin = nullptr; + // (If there's any characters _after_ the null we'll just crash.) + break; + + case 'b': // Backspace. + PQXX_UNLIKELY + *write++ = '\b'; + break; + case 'f': // Form feed + PQXX_UNLIKELY + *write++ = '\f'; + break; + case 'n': // Line feed. + *write++ = '\n'; + break; + case 'r': // Carriage return. + *write++ = '\r'; + break; + case 't': // Horizontal tab. + *write++ = '\t'; + break; + case 'v': // Vertical tab. + *write++ = '\v'; + break; + + default: + PQXX_LIKELY + // Regular character ("self-escaped"). + *write++ = c; + break; + } + } + break; + + PQXX_LIKELY + default: *write++ = c; break; + } + } + else + { + // Multi-byte sequence. Never treated specially, so just append. + while (read < glyph_end) *write++ = *read++; + } + } + + // End the last field here. + if (field_begin == nullptr) + { + m_fields.emplace_back(); + } + else + { + m_fields.push_back(zview{field_begin, write - field_begin}); + *write++ = '\0'; + } + + // DO NOT shrink m_row to fit. We're carrying string_views pointing into + // the buffer. (Also, how useful would shrinking really be?) +} + + +std::vector const *pqxx::stream_from::read_row() & +{ + parse_line(); + return m_finished ? nullptr : &m_fields; +} diff --git a/ext/libpqxx-7.7.3/src/stream_to.cxx b/ext/libpqxx-7.7.3/src/stream_to.cxx new file mode 100644 index 000000000..1693ba377 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/stream_to.cxx @@ -0,0 +1,170 @@ +/** Implementation of the pqxx::stream_to class. + * + * pqxx::stream_to enables optimized batch updates to a database table. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/gates/connection-stream_to.hxx" +#include "pqxx/stream_from.hxx" +#include "pqxx/stream_to.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace +{ +using namespace std::literals; + +void begin_copy( + pqxx::transaction_base &tx, std::string_view table, std::string_view columns) +{ + tx.exec0( + std::empty(columns) ? + pqxx::internal::concat("COPY "sv, table, " FROM STDIN"sv) : + pqxx::internal::concat( + "COPY "sv, table, "("sv, columns, ") FROM STDIN"sv)); +} +} // namespace + + +pqxx::stream_to::~stream_to() noexcept +{ + try + { + complete(); + } + catch (std::exception const &e) + { + reg_pending_error(e.what()); + } +} + + +void pqxx::stream_to::write_raw_line(std::string_view text) +{ + internal::gate::connection_stream_to{m_trans.conn()}.write_copy_line(text); +} + + +void pqxx::stream_to::write_buffer() +{ + if (not std::empty(m_buffer)) + { + // In append_to_buffer() we write a tab after each field. We only want a + // tab _between_ fields. Remove that last one. + assert(m_buffer[std::size(m_buffer) - 1] == '\t'); + m_buffer.resize(std::size(m_buffer) - 1); + } + write_raw_line(m_buffer); + m_buffer.clear(); +} + + +pqxx::stream_to &pqxx::stream_to::operator<<(stream_from &tr) +{ + while (tr) + { + const auto [line, size] = tr.get_raw_line(); + if (line.get() == nullptr) + break; + write_raw_line(std::string_view{line.get(), size}); + } + return *this; +} + + +pqxx::stream_to::stream_to( + transaction_base &tx, std::string_view path, std::string_view columns) : + transaction_focus{tx, s_classname, path}, + m_scanner{get_glyph_scanner( + pqxx::internal::enc_group(tx.conn().encoding_id()))} +{ + begin_copy(tx, path, columns); + register_me(); +} + + +void pqxx::stream_to::complete() +{ + if (!m_finished) + { + m_finished = true; + unregister_me(); + internal::gate::connection_stream_to{m_trans.conn()}.end_copy_write(); + } +} + + +/// Return escape letter for c's backslash sequence, or 0 if not needed. +/** The API is a bit weird: you pass the width of the character, and its first + * byte. That's because we never need to escape a multibyte character anyway. + */ +constexpr char escape(std::size_t width, char c) +{ + if (width == 1u) + switch (c) + { + case '\b': return 'b'; + case '\f': return 'f'; + case '\n': return 'n'; + case '\r': return 'r'; + case '\t': return 't'; + case '\v': return 'v'; + case '\\': return '\\'; + } + + PQXX_LIKELY + return '\0'; +} + + +void pqxx::stream_to::escape_field_to_buffer(std::string_view data) +{ + if (not std::empty(data)) + { + // Mark the beginning of a stretch that we can copy into our buffer in one + // go. It feels like a waste to invoke generic multi-byte copies for every + // individual character in this loop, most of them actually probably only + // one byte long. + std::size_t begin_stretch{0}; + + std::size_t begin_char{0}, end; + // XXX: find_char<'\b', '\f', '\n', '\r', '\t', \v', '\\'>(). + for (end = m_scanner(std::data(data), std::size(data), begin_char); + begin_char < std::size(data); begin_char = end, + end = m_scanner(std::data(data), std::size(data), begin_char)) + { + // Escape sequence letter, if needed. + char const esc{escape(end - begin_char, data[begin_char])}; + if (esc != '\0') + { + // This character needs escaping. So, it ends any trivially copyable + // stretch that we may have been having. + + // Copy the stretch we've built up into our buffer. + m_buffer.append( + std::data(data) + begin_stretch, begin_char - begin_stretch); + + // Escape the current character. + m_buffer.push_back('\\'); + m_buffer.push_back(esc); + + // Start a new stretch, right after the current character. + begin_stretch = end; + } + } + // Copy the final stretch. + m_buffer.append( + std::data(data) + begin_stretch, begin_char - begin_stretch); + } + m_buffer.push_back('\t'); +} diff --git a/ext/libpqxx-7.7.3/src/subtransaction.cxx b/ext/libpqxx-7.7.3/src/subtransaction.cxx new file mode 100644 index 000000000..186ad13e8 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/subtransaction.cxx @@ -0,0 +1,68 @@ +/** Implementation of the pqxx::subtransaction class. + * + * pqxx::transaction is a nested transaction, i.e. one within a transaction + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/connection.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/subtransaction.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace +{ +using namespace std::literals; +constexpr std::string_view class_name{"subtransaction"sv}; +} // namespace + + +pqxx::subtransaction::subtransaction( + dbtransaction &t, std::string_view tname) : + transaction_focus{t, class_name, t.conn().adorn_name(tname)}, + // We can't initialise the rollback command here, because we don't yet + // have a full object to implement quoted_name(). + dbtransaction{t.conn(), tname, std::shared_ptr{}} +{ + set_rollback_cmd(std::make_shared( + internal::concat("ROLLBACK TO SAVEPOINT ", quoted_name()))); + direct_exec(std::make_shared( + internal::concat("SAVEPOINT ", quoted_name()))); +} + + +namespace +{ +using dbtransaction_ref = pqxx::dbtransaction &; +} + + +pqxx::subtransaction::subtransaction( + subtransaction &t, std::string_view tname) : + subtransaction(dbtransaction_ref(t), tname) +{} + + +pqxx::subtransaction::~subtransaction() noexcept +{ + close(); +} + + +void pqxx::subtransaction::do_commit() +{ + direct_exec(std::make_shared( + internal::concat("RELEASE SAVEPOINT ", quoted_name()))); +} diff --git a/ext/libpqxx-7.7.3/src/time.cxx b/ext/libpqxx-7.7.3/src/time.cxx new file mode 100644 index 000000000..6b3ffdb1c --- /dev/null +++ b/ext/libpqxx-7.7.3/src/time.cxx @@ -0,0 +1,226 @@ +/** Implementation of date/time support. + */ +#include "pqxx-source.hxx" + +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/time.hxx" + +#include "pqxx/internal/header-post.hxx" + +// std::chrono::year_month_day is C++20, so let's worry a bit less about C++17 +// compatibility in this file. +#if defined(PQXX_HAVE_YEAR_MONTH_DAY) +namespace +{ +using namespace std::literals; + + +/// Render the numeric part of a year value into a buffer. +/** Converts the year from "common era" (with a Year Zero) to "anno domini" + * (without a Year Zero). + * + * Doesn't render the sign. When you're rendering a date, you indicate a + * negative year by suffixing "BC" at the very end. + * + * Where @c string_traits::into_buf() returns a pointer to the position right + * after the terminating zero, this function returns a pointer to the character + * right after the last digit. (It may or may not write a terminating zero at + * that position itself.) + */ +inline char * +year_into_buf(char *begin, char *end, std::chrono::year const &value) +{ + int const y{value}; + if (y == int{(std::chrono::year::min)()}) + { + // This is an evil special case: C++ year -32767 translates to 32768 BC, + // which is a number we can't fit into a short. At the moment postgres + // doesn't handle years before 4713 BC, but who knows, right? + static_assert(int{(std::chrono::year::min)()} == -32767); + constexpr auto hardcoded{"32768"sv}; + PQXX_UNLIKELY + begin += hardcoded.copy(begin, std::size(hardcoded)); + } + else + { + // C++ std::chrono::year has a year zero. PostgreSQL does not. So, C++ + // year zero is 1 BC in the postgres calendar; C++ 1 BC is postgres 2 BC, + // and so on. + auto const absy{static_cast(std::abs(y) + int{y <= 0})}; + + // PostgreSQL requires year input to be at least 3 digits long, or it + // won't be able to deduce the date format correctly. However on output + // it always writes years as at least 4 digits, and we'll do the same. + // Dates and times are a dirty, dirty business. + if (absy < 1000) + { + PQXX_UNLIKELY + *begin++ = '0'; + if (absy < 100) + *begin++ = '0'; + if (absy < 10) + *begin++ = '0'; + } + begin = pqxx::string_traits::into_buf(begin, end, absy) - 1; + } + return begin; +} + + +/// Parse the numeric part of a year value. +inline int year_from_buf(std::string_view text) +{ + if (std::size(text) < 4) + throw pqxx::conversion_error{ + pqxx::internal::concat("Year field is too small: '", text, "'.")}; + // Parse as int, so we can accommodate 32768 BC which won't fit in a short + // as-is, but equates to 32767 BCE which will. + int const year{pqxx::string_traits::from_string(text)}; + if (year <= 0) + throw pqxx::conversion_error{ + pqxx::internal::concat("Bad year: '", text, "'.")}; + return year; +} + + +/// Render a valid 1-based month number into a buffer. +/* Where @c string_traits::into_buf() returns a pointer to the position right + * after the terminating zero, this function returns a pointer to the character + * right after the last digit. (It may or may not write a terminating zero at + * that position itself.) + */ +inline static char * +month_into_buf(char *begin, std::chrono::month const &value) +{ + unsigned const m{value}; + if (m >= 10) + *begin = '1'; + else + *begin = '0'; + ++begin; + *begin++ = pqxx::internal::number_to_digit(static_cast(m % 10)); + return begin; +} + + +/// Parse a 1-based month value. +inline std::chrono::month month_from_string(std::string_view text) +{ + if ( + not pqxx::internal::is_digit(text[0]) or + not pqxx::internal::is_digit(text[1])) + throw pqxx::conversion_error{ + pqxx::internal::concat("Invalid month: '", text, "'.")}; + return std::chrono::month{unsigned( + (10 * pqxx::internal::digit_to_number(text[0])) + + pqxx::internal::digit_to_number(text[1]))}; +} + + +/// Render a valid 1-based day-of-month value into a buffer. +inline char *day_into_buf(char *begin, std::chrono::day const &value) +{ + unsigned d{value}; + *begin++ = pqxx::internal::number_to_digit(static_cast(d / 10)); + *begin++ = pqxx::internal::number_to_digit(static_cast(d % 10)); + return begin; +} + + +/// Parse a 1-based day-of-month value. +inline std::chrono::day day_from_string(std::string_view text) +{ + if ( + not pqxx::internal::is_digit(text[0]) or + not pqxx::internal::is_digit(text[1])) + throw pqxx::conversion_error{ + pqxx::internal::concat("Bad day in date: '", text, "'.")}; + std::chrono::day const d{unsigned( + (10 * pqxx::internal::digit_to_number(text[0])) + + pqxx::internal::digit_to_number(text[1]))}; + if (not d.ok()) + throw pqxx::conversion_error{ + pqxx::internal::concat("Bad day in date: '", text, "'.")}; + return d; +} + + +/// Look for the dash separating year and month. +/** Assumes that @c text is nonempty. + */ +inline std::size_t find_year_month_separator(std::string_view text) noexcept +{ + // We're looking for a dash. PostgreSQL won't output a negative year, so + // no worries about a leading dash. We could start searching at offset 4, + // but starting at the beginning produces more helpful error messages for + // malformed years. + std::size_t here; + for (here = 0; here < std::size(text) and text[here] != '-'; ++here) + ; + return here; +} + + +/// Componse generic "invalid date" message for given (invalid) date text. +std::string make_parse_error(std::string_view text) +{ + return pqxx::internal::concat("Invalid date: '", text, "'."); +} +} // namespace + + +namespace pqxx +{ +char *string_traits::into_buf( + char *begin, char *end, std::chrono::year_month_day const &value) +{ + if (std::size_t(end - begin) < size_buffer(value)) + throw conversion_overrun{"Not enough room in buffer for date."}; + begin = year_into_buf(begin, end, value.year()); + *begin++ = '-'; + begin = month_into_buf(begin, value.month()); + *begin++ = '-'; + begin = day_into_buf(begin, value.day()); + if (int{value.year()} <= 0) + { + PQXX_UNLIKELY + begin += s_bc.copy(begin, std::size(s_bc)); + } + *begin++ = '\0'; + return begin; +} + + +std::chrono::year_month_day +string_traits::from_string(std::string_view text) +{ + // We can't just re-use the std::chrono::year conversions, because the "BC" + // suffix comes at the very end. + if (std::size(text) < 9) + throw conversion_error{make_parse_error(text)}; + bool const is_bc{text.ends_with(s_bc)}; + if (is_bc) + PQXX_UNLIKELY + text = text.substr(0, std::size(text) - std::size(s_bc)); + auto const ymsep{find_year_month_separator(text)}; + if ((std::size(text) - ymsep) != 6) + throw conversion_error{make_parse_error(text)}; + auto const base_year{ + year_from_buf(std::string_view{std::data(text), ymsep})}; + if (base_year == 0) + throw conversion_error{"Year zero conversion."}; + std::chrono::year const y{is_bc ? (-base_year + 1) : base_year}; + auto const m{month_from_string(text.substr(ymsep + 1, 2))}; + if (text[ymsep + 3] != '-') + throw conversion_error{make_parse_error(text)}; + auto const d{day_from_string(text.substr(ymsep + 4, 2))}; + std::chrono::year_month_day const date{y, m, d}; + if (not date.ok()) + throw conversion_error{make_parse_error(text)}; + return date; +} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/src/transaction.cxx b/ext/libpqxx-7.7.3/src/transaction.cxx new file mode 100644 index 000000000..61ed29ef7 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/transaction.cxx @@ -0,0 +1,107 @@ +/** Implementation of the pqxx::transaction class. + * + * pqxx::transaction represents a regular database transaction. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/connection.hxx" +#include "pqxx/result.hxx" +#include "pqxx/transaction.hxx" + +#include "pqxx/internal/header-post.hxx" + + +pqxx::internal::basic_transaction::basic_transaction( + connection &c, zview begin_command, std::string_view tname) : + dbtransaction(c, tname) +{ + register_transaction(); + direct_exec(begin_command); +} + + +pqxx::internal::basic_transaction::basic_transaction( + connection &c, zview begin_command, std::string &&tname) : + dbtransaction(c, std::move(tname)) +{ + register_transaction(); + direct_exec(begin_command); +} + + +pqxx::internal::basic_transaction::basic_transaction( + connection &c, zview begin_command) : + dbtransaction(c) +{ + register_transaction(); + direct_exec(begin_command); +} + + +// This should stop the compiler from generating the same vtables and +// destructor in multiple translation units. More importantly, if we don't do +// this, the sanitisers in g++ 7 and clang++ 6 complain about pointers to +// dbtransaction actually pointing to basic_transaction. Which is odd, in that +// any basic_transaction pointer should also be a dbtransaction pointer. But, +// apparently the vtable isn't the right one. +pqxx::internal::basic_transaction::~basic_transaction() noexcept = default; + + +void pqxx::internal::basic_transaction::do_commit() +{ + static auto const commit_q{std::make_shared("COMMIT"sv)}; + try + { + direct_exec(commit_q); + } + catch (statement_completion_unknown const &e) + { + // Outcome of "commit" is unknown. This is a disaster: we don't know the + // resulting state of the database. + process_notice(internal::concat(e.what(), "\n")); + + std::string msg{internal::concat( + "WARNING: Commit of transaction '", name(), + "' is unknown. " + "There is no way to tell whether the transaction succeeded " + "or was aborted except to check manually.\n")}; + process_notice(msg); + // Strip newline. It was only needed for process_notice(). + msg.pop_back(); + throw in_doubt_error{std::move(msg)}; + } + catch (std::exception const &e) + { + if (not conn().is_open()) + { + // We've lost the connection while committing. There is just no way of + // telling what happened on the other end. >8-O + process_notice(internal::concat(e.what(), "\n")); + + auto msg{internal::concat( + "WARNING: Connection lost while committing transaction '", name(), + "'. There is no way to tell whether the transaction succeeded " + "or was aborted except to check manually.\n")}; + process_notice(msg); + // Strip newline. It was only needed for process_notice(). + msg.pop_back(); + throw in_doubt_error{std::move(msg)}; + } + else + { + // Commit failed--probably due to a constraint violation or something + // similar. + throw; + } + } +} diff --git a/ext/libpqxx-7.7.3/src/transaction_base.cxx b/ext/libpqxx-7.7.3/src/transaction_base.cxx new file mode 100644 index 000000000..c7d01ac08 --- /dev/null +++ b/ext/libpqxx-7.7.3/src/transaction_base.cxx @@ -0,0 +1,539 @@ +/** Common code and definitions for the transaction classes. + * + * pqxx::transaction_base defines the interface for any abstract class that + * represents a database transaction. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/connection.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/encodings.hxx" +#include "pqxx/internal/gates/connection-transaction.hxx" +#include "pqxx/internal/gates/transaction-transaction_focus.hxx" +#include "pqxx/result.hxx" +#include "pqxx/transaction_base.hxx" +#include "pqxx/transaction_focus.hxx" + +#include "pqxx/internal/header-post.hxx" + + +using namespace std::literals; + +namespace +{ +/// Return a query pointer for the command "ROLLBACK". +/** Concentrates constructions so as to minimise the number of allocations. + * This way, the string gets allocated once and then all subsequent invocations + * copy shared_ptr instances to the same string. + */ +std::shared_ptr make_rollback_cmd() +{ + static auto const cmd{std::make_shared("ROLLBACK")}; + return cmd; +} +} // namespace + +pqxx::transaction_base::transaction_base(connection &c) : + m_conn{c}, m_rollback_cmd{make_rollback_cmd()} +{} + + +pqxx::transaction_base::transaction_base( + connection &c, std::string_view tname) : + m_conn{c}, m_name{tname}, m_rollback_cmd{make_rollback_cmd()} +{} + + +pqxx::transaction_base::~transaction_base() +{ + try + { + if (not std::empty(m_pending_error)) + PQXX_UNLIKELY + process_notice( + internal::concat("UNPROCESSED ERROR: ", m_pending_error, "\n")); + + if (m_registered) + { + m_conn.process_notice( + internal::concat(description(), " was never closed properly!\n")); + pqxx::internal::gate::connection_transaction{conn()} + .unregister_transaction(this); + } + } + catch (std::exception const &e) + { + try + { + process_notice(internal::concat(e.what(), "\n")); + } + catch (std::exception const &) + { + process_notice(e.what()); + } + } +} + + +void pqxx::transaction_base::register_transaction() +{ + pqxx::internal::gate::connection_transaction{conn()}.register_transaction( + this); + m_registered = true; +} + + +void pqxx::transaction_base::commit() +{ + check_pending_error(); + + // Check previous status code. Caller should only call this function if + // we're in "implicit" state, but multiple commits are silently accepted. + switch (m_status) + { + case status::active: // Just fine. This is what we expect. + break; + + case status::aborted: + throw usage_error{internal::concat( + "Attempt to commit previously aborted ", description())}; + + case status::committed: + // Transaction has been committed already. This is not exactly proper + // behaviour, but throwing an exception here would only give the impression + // that an abort is needed--which would only confuse things further at this + // stage. + // Therefore, multiple commits are accepted, though under protest. + m_conn.process_notice( + internal::concat(description(), " committed more than once.\n")); + return; + + case status::in_doubt: + // Transaction may or may not have been committed. The only thing we can + // really do is keep telling the caller that the transaction is in doubt. + throw in_doubt_error{internal::concat( + description(), " committed again while in an indeterminate state.")}; + + default: throw internal_error{"pqxx::transaction: invalid status code."}; + } + + // Tricky one. If stream is nested in transaction but inside the same scope, + // the commit() will come before the stream is closed. Which means the + // commit is premature. Punish this swiftly and without fail to discourage + // the habit from forming. + if (m_focus != nullptr) + throw failure{internal::concat( + "Attempt to commit ", description(), " with ", m_focus->description(), + " still open.")}; + + // Check that we're still connected (as far as we know--this is not an + // absolute thing!) before trying to commit. If the connection was broken + // already, the commit would fail anyway but this way at least we don't + // remain in-doubt as to whether the backend got the commit order at all. + if (not m_conn.is_open()) + throw broken_connection{ + "Broken connection to backend; cannot complete transaction."}; + + try + { + do_commit(); + m_status = status::committed; + } + catch (in_doubt_error const &) + { + m_status = status::in_doubt; + throw; + } + catch (std::exception const &) + { + m_status = status::aborted; + throw; + } + + close(); +} + + +void pqxx::transaction_base::do_abort() +{ + if (m_rollback_cmd) + direct_exec(m_rollback_cmd); +} + + +void pqxx::transaction_base::abort() +{ + // Check previous status code. Quietly accept multiple aborts to + // simplify emergency bailout code. + switch (m_status) + { + case status::active: + try + { + do_abort(); + } + catch (std::exception const &e) + { + m_conn.process_notice(internal::concat(e.what(), "\n")); + } + break; + + case status::aborted: return; + + case status::committed: + throw usage_error{internal::concat( + "Attempt to abort previously committed ", description())}; + + case status::in_doubt: + // Aborting an in-doubt transaction is probably a reasonably sane response + // to an insane situation. Log it, but do not fail. + m_conn.process_notice(internal::concat( + "Warning: ", description(), + " aborted after going into indeterminate state; " + "it may have been executed anyway.\n")); + return; + + default: throw internal_error{"Invalid transaction status."}; + } + + m_status = status::aborted; + close(); +} + + +std::string PQXX_COLD pqxx::transaction_base::quote_raw(zview bin) const +{ + return conn().quote(binary_cast(bin)); +} + + +namespace +{ +/// Guard command execution against clashes with pipelines and such. +/** A transaction can have only one focus at a time. Command execution is the + * most basic example of a transaction focus. + */ +class PQXX_PRIVATE command : pqxx::transaction_focus +{ +public: + command(pqxx::transaction_base &tx, std::string_view oname) : + transaction_focus{tx, "command"sv, oname} + { + register_me(); + } + + ~command() { unregister_me(); } +}; +} // namespace + +pqxx::result +pqxx::transaction_base::exec(std::string_view query, std::string_view desc) +{ + check_pending_error(); + + command cmd{*this, desc}; + + switch (m_status) + { + case status::active: break; + + case status::committed: + case status::aborted: + case status::in_doubt: { + std::string const n{ + std::empty(desc) ? "" : internal::concat("'", desc, "' ")}; + + throw usage_error{internal::concat( + "Could not execute command ", n, ": transaction is already closed.")}; + } + + default: throw internal_error{"pqxx::transaction: invalid status code."}; + } + + return direct_exec(query, desc); +} + + +pqxx::result pqxx::transaction_base::exec_n( + result::size_type rows, zview query, std::string_view desc) +{ +#include "pqxx/internal/ignore-deprecated-pre.hxx" + result const r{exec(query, desc)}; +#include "pqxx/internal/ignore-deprecated-post.hxx" + if (std::size(r) != rows) + { + std::string const N{ + std::empty(desc) ? "" : internal::concat("'", desc, "'")}; + throw unexpected_rows{internal::concat( + "Expected ", rows, " row(s) of data from query ", N, ", got ", + std::size(r), ".")}; + } + return r; +} + + +void pqxx::transaction_base::check_rowcount_prepared( + zview statement, result::size_type expected_rows, + result::size_type actual_rows) +{ + if (actual_rows != expected_rows) + throw unexpected_rows{internal::concat( + "Expected ", expected_rows, " row(s) of data from prepared statement '", + statement, "', got ", actual_rows, ".")}; +} + + +void pqxx::transaction_base::check_rowcount_params( + std::size_t expected_rows, std::size_t actual_rows) +{ + if (actual_rows != expected_rows) + throw unexpected_rows{internal::concat( + "Expected ", expected_rows, + " row(s) of data from parameterised query, got ", actual_rows, ".")}; +} + + +pqxx::result pqxx::transaction_base::internal_exec_prepared( + zview statement, internal::c_params const &args) +{ + command cmd{*this, statement}; + return pqxx::internal::gate::connection_transaction{conn()}.exec_prepared( + statement, args); +} + + +pqxx::result pqxx::transaction_base::internal_exec_params( + zview query, internal::c_params const &args) +{ + command cmd{*this, query}; + return pqxx::internal::gate::connection_transaction{conn()}.exec_params( + query, args); +} + + +void pqxx::transaction_base::set_variable( + std::string_view var, std::string_view value) +{ +#include "pqxx/internal/ignore-deprecated-pre.hxx" + conn().set_variable(var, value); +#include "pqxx/internal/ignore-deprecated-post.hxx" +} + + +std::string pqxx::transaction_base::get_variable(std::string_view var) +{ +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return conn().get_variable(var); +#include "pqxx/internal/ignore-deprecated-post.hxx" +} + + +void pqxx::transaction_base::close() noexcept +{ + try + { + try + { + check_pending_error(); + } + catch (std::exception const &e) + { + m_conn.process_notice(e.what()); + } + + if (m_registered) + { + m_registered = false; + pqxx::internal::gate::connection_transaction{conn()} + .unregister_transaction(this); + } + + if (m_status != status::active) + return; + + if (m_focus != nullptr) + PQXX_UNLIKELY + m_conn.process_notice(internal::concat( + "Closing ", description(), " with ", m_focus->description(), + " still open.\n")); + + try + { + abort(); + } + catch (std::exception const &e) + { + m_conn.process_notice(e.what()); + } + } + catch (std::exception const &e) + { + try + { + m_conn.process_notice(e.what()); + } + catch (std::exception const &) + {} + } +} + + +namespace +{ +[[nodiscard]] std::string_view +get_classname(pqxx::transaction_focus const *focus) +{ + return (focus == nullptr) ? ""sv : focus->classname(); +} + + +[[nodiscard]] std::string_view +get_obj_name(pqxx::transaction_focus const *focus) +{ + return (focus == nullptr) ? ""sv : focus->name(); +} +} // namespace + + +void pqxx::transaction_base::register_focus(transaction_focus *new_focus) +{ + internal::check_unique_register( + m_focus, get_classname(m_focus), get_obj_name(m_focus), new_focus, + get_classname(new_focus), get_obj_name(new_focus)); + m_focus = new_focus; +} + + +void pqxx::transaction_base::unregister_focus( + transaction_focus *new_focus) noexcept +{ + try + { + pqxx::internal::check_unique_unregister( + m_focus, get_classname(m_focus), get_obj_name(m_focus), new_focus, + get_classname(new_focus), get_obj_name(new_focus)); + m_focus = nullptr; + } + catch (std::exception const &e) + { + m_conn.process_notice(internal::concat(e.what(), "\n")); + } +} + + +pqxx::result pqxx::transaction_base::direct_exec( + std::string_view cmd, std::string_view desc) +{ + check_pending_error(); + return pqxx::internal::gate::connection_transaction{conn()}.exec(cmd, desc); +} + + +pqxx::result pqxx::transaction_base::direct_exec( + std::shared_ptr cmd, std::string_view desc) +{ + check_pending_error(); + return pqxx::internal::gate::connection_transaction{conn()}.exec(cmd, desc); +} + + +void pqxx::transaction_base::register_pending_error(zview err) noexcept +{ + if (std::empty(m_pending_error) and not std::empty(err)) + { + try + { + m_pending_error = err; + } + catch (std::exception const &e) + { + try + { + PQXX_UNLIKELY + process_notice("UNABLE TO PROCESS ERROR\n"); + process_notice(e.what()); + process_notice("ERROR WAS:"); + process_notice(err); + } + catch (...) + {} + } + } +} + + +void pqxx::transaction_base::register_pending_error(std::string &&err) noexcept +{ + if (std::empty(m_pending_error) and not std::empty(err)) + { + try + { + m_pending_error = std::move(err); + } + catch (std::exception const &e) + { + try + { + PQXX_UNLIKELY + process_notice("UNABLE TO PROCESS ERROR\n"); + process_notice(e.what()); + process_notice("ERROR WAS:"); + process_notice(err); + } + catch (...) + {} + } + } +} + + +void pqxx::transaction_base::check_pending_error() +{ + if (not std::empty(m_pending_error)) + { + std::string err; + err.swap(m_pending_error); + throw failure{err}; + } +} + + +std::string pqxx::transaction_base::description() const +{ + return internal::describe_object("transaction", name()); +} + + +void pqxx::transaction_focus::register_me() +{ + pqxx::internal::gate::transaction_transaction_focus{m_trans}.register_focus( + this); + m_registered = true; +} + + +void pqxx::transaction_focus::unregister_me() noexcept +{ + pqxx::internal::gate::transaction_transaction_focus{m_trans} + .unregister_focus(this); + m_registered = false; +} + + +void pqxx::transaction_focus::reg_pending_error( + std::string const &err) noexcept +{ + pqxx::internal::gate::transaction_transaction_focus{m_trans} + .register_pending_error(err); +} diff --git a/ext/libpqxx-7.7.3/src/util.cxx b/ext/libpqxx-7.7.3/src/util.cxx new file mode 100644 index 000000000..fe859bd2a --- /dev/null +++ b/ext/libpqxx-7.7.3/src/util.cxx @@ -0,0 +1,194 @@ +/** Various utility functions. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include +#include +#include +#include +#include + +extern "C" +{ +#include +} + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/except.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/util.hxx" + +#include "pqxx/internal/header-post.hxx" + + +using namespace std::literals; + +pqxx::thread_safety_model PQXX_COLD pqxx::describe_thread_safety() +{ + thread_safety_model model; + model.safe_libpq = (PQisthreadsafe() != 0); + // Sadly I'm not aware of any way to avoid this just yet. + model.safe_kerberos = false; + + model.description = internal::concat( + (model.safe_libpq ? ""sv : + "Using a libpq build that is not thread-safe.\n"sv), + (model.safe_kerberos ? + ""sv : + "Kerberos is not thread-safe. If your application uses Kerberos, " + "protect all calls to Kerberos or libpqxx using a global lock.\n"sv)); + return model; +} + + +std::string pqxx::internal::describe_object( + std::string_view class_name, std::string_view obj_name) +{ + if (std::empty(obj_name)) + return std::string{class_name}; + else + return pqxx::internal::concat(class_name, " '", obj_name, "'"); +} + + +void pqxx::internal::check_unique_register( + void const *old_guest, std::string_view old_class, std::string_view old_name, + void const *new_guest, std::string_view new_class, std::string_view new_name) +{ + if (new_guest == nullptr) + throw internal_error{"Null pointer registered."}; + + if (old_guest != nullptr) + throw usage_error{ + (old_guest == new_guest) ? + concat("Started twice: ", describe_object(old_class, old_name), ".") : + concat( + "Started new ", describe_object(new_class, new_name), " while ", + describe_object(new_class, new_name), " was still active.")}; +} + + +void pqxx::internal::check_unique_unregister( + void const *old_guest, std::string_view old_class, std::string_view old_name, + void const *new_guest, std::string_view new_class, std::string_view new_name) +{ + if (new_guest != old_guest) + { + PQXX_UNLIKELY + if (new_guest == nullptr) + throw usage_error{concat( + "Expected to close ", describe_object(old_class, old_name), + ", but got null pointer instead.")}; + if (old_guest == nullptr) + throw usage_error{concat( + "Closed while not open: ", describe_object(new_class, new_name))}; + else + throw usage_error{concat( + "Closed ", describe_object(new_class, new_name), + "; expected to close ", describe_object(old_class, old_name))}; + } +} + + +namespace +{ +constexpr char hex_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + +/// Translate a number (must be between 0 and 16 exclusive) to a hex digit. +constexpr char hex_digit(int c) noexcept +{ + return hex_digits[c]; +} + + +/// Translate a hex digit to a nibble. Return -1 if it's not a valid digit. +constexpr int nibble(int c) noexcept +{ + if (c >= '0' and c <= '9') + PQXX_LIKELY + return c - '0'; + else if (c >= 'a' and c <= 'f') return 10 + (c - 'a'); + else if (c >= 'A' and c <= 'F') return 10 + (c - 'A'); + else return -1; +} +} // namespace + + +void pqxx::internal::esc_bin( + std::basic_string_view binary_data, char buffer[]) noexcept +{ + auto here{buffer}; + *here++ = '\\'; + *here++ = 'x'; + + for (auto const byte : binary_data) + { + auto uc{static_cast(byte)}; + *here++ = hex_digit(uc >> 4); + *here++ = hex_digit(uc & 0x0f); + } + + // (No need to increment further. Facebook's "infer" complains if we do.) + *here = '\0'; +} + + +std::string +pqxx::internal::esc_bin(std::basic_string_view binary_data) +{ + auto const bytes{size_esc_bin(std::size(binary_data))}; + std::string buf; + buf.resize(bytes); + esc_bin(binary_data, buf.data()); + // Strip off the trailing zero. + buf.resize(bytes - 1); + return buf; +} + + +void pqxx::internal::unesc_bin( + std::string_view escaped_data, std::byte buffer[]) +{ + auto const in_size{std::size(escaped_data)}; + if (in_size < 2) + throw pqxx::failure{"Binary data appears truncated."}; + if ((in_size % 2) != 0) + throw pqxx::failure{"Invalid escaped binary length."}; + char const *in{escaped_data.data()}; + char const *const end{in + in_size}; + if (*in++ != '\\' or *in++ != 'x') + throw pqxx::failure( + "Escaped binary data did not start with '\\x'`. Is the server or libpq " + "too old?"); + auto out{buffer}; + while (in != end) + { + int hi{nibble(*in++)}; + if (hi < 0) + throw pqxx::failure{"Invalid hex-escaped data."}; + int lo{nibble(*in++)}; + if (lo < 0) + throw pqxx::failure{"Invalid hex-escaped data."}; + *out++ = static_cast((hi << 4) | lo); + } +} + + +std::basic_string +pqxx::internal::unesc_bin(std::string_view escaped_data) +{ + auto const bytes{size_unesc_bin(std::size(escaped_data))}; + std::basic_string buf; + buf.resize(bytes); + unesc_bin(escaped_data, buf.data()); + return buf; +} diff --git a/ext/libpqxx-7.7.3/src/version.cxx b/ext/libpqxx-7.7.3/src/version.cxx new file mode 100644 index 000000000..e216ecd4f --- /dev/null +++ b/ext/libpqxx-7.7.3/src/version.cxx @@ -0,0 +1,27 @@ +/** Version check. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#include "pqxx-source.hxx" + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/version.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace pqxx::internal +{ +// One, single definition of this function. If a call fails to link, and +// (some) other calls do link, then the libpqxx binary was built against a +// different libpqxx version than the code which is being linked against it. +PQXX_LIBEXPORT int PQXX_VERSION_CHECK() noexcept +{ + return 0; +} +} // namespace pqxx::internal diff --git a/ext/libpqxx-7.7.3/src/wait.cxx b/ext/libpqxx-7.7.3/src/wait.cxx new file mode 100644 index 000000000..276d9659e --- /dev/null +++ b/ext/libpqxx-7.7.3/src/wait.cxx @@ -0,0 +1,136 @@ +/** Functions that wait. + */ +#include "pqxx-source.hxx" + +// The header is still broken on MinGW. :-( +#if defined(PQXX_HAVE_SLEEP_FOR) +# include +#endif + +// For WSAPoll(): +#if __has_include() +# include +# define PQXX_HAVE_SELECT +#endif +#if __has_include() +# include +#endif +#if __has_include() +# include +#endif + +// For poll(): +#if __has_include() +# include +#endif + +// For select() on recent POSIX systems. +#if __has_include() +# include +# define PQXX_HAVE_SELECT +#endif + +// For select() on some older POSIX systems. +#if __has_include() +# include +# define PQXX_HAVE_SELECT +#endif +#if __has_include() +# include +#endif +#if __has_include() +# include +#endif + + +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/internal/wait.hxx" +#include "pqxx/util.hxx" + +#include "pqxx/internal/header-post.hxx" + + +namespace +{ +template T to_milli(unsigned seconds, unsigned microseconds) +{ + return pqxx::check_cast( + (seconds * 1000) + (microseconds / 1000), + "Wait timeout value out of bounds."); +} + + +#if defined(PQXX_HAVE_SELECT) +/// Set a bit on an fd_set. +[[maybe_unused]] void set_fdbit(fd_set &bits, int fd) +{ +# ifdef _MSC_VER +// Suppress pointless, unfixable warnings in Visual Studio. +# pragma warning(push) +# pragma warning(disable : 4389) // Signed/unsigned mismatch. +# pragma warning(disable : 4127) // Conditional expression is constant. +# endif + FD_SET(fd, &bits); +# ifdef _MSV_VER +// Restore prevalent warning settings. +# pragma warning(pop) +# endif +} +#endif +} // namespace + + +void pqxx::internal::wait_fd( + int fd, bool for_read, bool for_write, unsigned seconds, + unsigned microseconds) +{ +// WSAPoll is available in winsock2.h only for versions of Windows >= 0x0600 +#if defined(_WIN32) && (_WIN32_WINNT >= 0x0600) + short const events{static_cast( + (for_read ? POLLRDNORM : 0) | (for_write ? POLLWRNORM : 0))}; + WSAPOLLFD fdarray{SOCKET(fd), events, 0}; + WSAPoll(&fdarray, 1u, to_milli(seconds, microseconds)); + // TODO: Check for errors. +#elif defined(PQXX_HAVE_POLL) + auto const events{static_cast( + POLLERR | POLLHUP | POLLNVAL | (for_read ? POLLIN : 0) | + (for_write ? POLLOUT : 0))}; + pollfd pfd{fd, events, 0}; + poll(&pfd, 1, to_milli(seconds, microseconds)); + // TODO: Check for errors. +#else + // No poll()? Our last option is select(). + fd_set read_fds; + FD_ZERO(&read_fds); + if (for_read) + set_fdbit(read_fds, fd); + + fd_set write_fds; + FD_ZERO(&write_fds); + if (for_write) + set_fdbit(write_fds, fd); + + fd_set except_fds; + FD_ZERO(&except_fds); + set_fdbit(except_fds, fd); + + timeval tv = {seconds, microseconds}; + select(fd + 1, &read_fds, &write_fds, &except_fds, &tv); + // TODO: Check for errors. +#endif +} + + +void PQXX_COLD pqxx::internal::wait_for(unsigned int microseconds) +{ +#if defined(PQXX_HAVE_SLEEP_FOR) + std::this_thread::sleep_for(std::chrono::microseconds{microseconds}); +#else + // MinGW still does not have a functioning header. Work around this + // using select(). + // Not worth optimising for though -- they'll have to fix it at some point. + timeval tv{microseconds / 1'000'000u, microseconds % 1'000'000u}; + select(0, nullptr, nullptr, nullptr, &tv); +#endif +} diff --git a/ext/libpqxx-7.7.3/test/CMakeLists.txt b/ext/libpqxx-7.7.3/test/CMakeLists.txt new file mode 100644 index 000000000..331631145 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/CMakeLists.txt @@ -0,0 +1,24 @@ +enable_testing() + +if(NOT PostgreSQL_FOUND) + find_package(PostgreSQL REQUIRED) +endif() + +file(GLOB TEST_SOURCES test*.cxx unit/test_*.cxx runner.cxx) + +add_executable(runner ${TEST_SOURCES}) +target_link_libraries(runner PUBLIC pqxx) +target_include_directories(runner PRIVATE ${PostgreSQL_INCLUDE_DIRS}) +add_test( + NAME runner + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMAND runner +) + +if(INSTALL_TEST) + install( + PROGRAMS runner + TYPE BIN + RENAME libpqxx-test-runner + ) +endif() diff --git a/ext/libpqxx-7.7.3/test/Makefile.am b/ext/libpqxx-7.7.3/test/Makefile.am new file mode 100644 index 000000000..e3ce4be7b --- /dev/null +++ b/ext/libpqxx-7.7.3/test/Makefile.am @@ -0,0 +1,129 @@ +# ############################################################################## +# AUTOMATICALLY GENERATED FILE -- DO NOT EDIT. +# +# This file is generated automatically by libpqxx's template2mak.py script, and +# will be rewritten from time to time. +# +# If you modify this file, chances are your modifications will be lost. +# +# The template2mak.py script should be available in the tools directory of the +# libpqxx source archive. +# +# Generated from template './test/Makefile.am.template'. +# ############################################################################## +# Makefile.am is generated automatically for automake whenever test programs are +# added to libpqxx. + +EXTRA_DIST = Makefile.am.template + +# Use the serial test runner, so tests don't get run in parallel. Otherwise, +# output from test failures will be hidden away in log files where we can't +# see them when running in a build slave. +AUTOMAKE_OPTIONS=serial-tests + + +AM_CPPFLAGS=-I$(top_builddir)/include -I$(top_srcdir)/include + +# Override automatically generated list of default includes. It contains only +# unnecessary entries, and incorrectly mentions include/pqxx directly. +DEFAULT_INCLUDES= + +noinst_HEADERS = test_helpers.hxx + +CLEANFILES=pqxxlo.txt +MAINTAINERCLEANFILES=Makefile.in + +#TESTS_ENVIRONMENT=PGDATABASE=libpqxx +# PGDATABASE, PGHOST, PGPORT, PGUSER + +runner_SOURCES = \ + test00.cxx \ + test01.cxx \ + test02.cxx \ + test04.cxx \ + test07.cxx \ + test10.cxx \ + test11.cxx \ + test13.cxx \ + test14.cxx \ + test16.cxx \ + test17.cxx \ + test18.cxx \ + test20.cxx \ + test21.cxx \ + test26.cxx \ + test29.cxx \ + test30.cxx \ + test32.cxx \ + test37.cxx \ + test39.cxx \ + test46.cxx \ + test56.cxx \ + test60.cxx \ + test61.cxx \ + test62.cxx \ + test69.cxx \ + test70.cxx \ + test71.cxx \ + test72.cxx \ + test74.cxx \ + test75.cxx \ + test76.cxx \ + test77.cxx \ + test78.cxx \ + test79.cxx \ + test82.cxx \ + test84.cxx \ + test87.cxx \ + test88.cxx \ + test89.cxx \ + test90.cxx \ + unit/test_array.cxx \ + unit/test_binarystring.cxx \ + unit/test_blob.cxx \ + unit/test_cancel_query.cxx \ + unit/test_column.cxx \ + unit/test_composite.cxx \ + unit/test_connection.cxx \ + unit/test_cursor.cxx \ + unit/test_encodings.cxx \ + unit/test_error_verbosity.cxx \ + unit/test_errorhandler.cxx \ + unit/test_escape.cxx \ + unit/test_exceptions.cxx \ + unit/test_field.cxx \ + unit/test_float.cxx \ + unit/test_largeobject.cxx \ + unit/test_nonblocking_connect.cxx \ + unit/test_notification.cxx \ + unit/test_pipeline.cxx \ + unit/test_prepared_statement.cxx \ + unit/test_range.cxx \ + unit/test_read_transaction.cxx \ + unit/test_result_iteration.cxx \ + unit/test_result_slicing.cxx \ + unit/test_row.cxx \ + unit/test_separated_list.cxx \ + unit/test_simultaneous_transactions.cxx \ + unit/test_sql_cursor.cxx \ + unit/test_stateless_cursor.cxx \ + unit/test_strconv.cxx \ + unit/test_stream_from.cxx \ + unit/test_stream_to.cxx \ + unit/test_string_conversion.cxx \ + unit/test_subtransaction.cxx \ + unit/test_test_helpers.cxx \ + unit/test_thread_safety_model.cxx \ + unit/test_time.cxx \ + unit/test_transaction.cxx \ + unit/test_transaction_base.cxx \ + unit/test_transaction_focus.cxx \ + unit/test_transactor.cxx \ + unit/test_type_name.cxx \ + unit/test_zview.cxx \ + runner.cxx + +runner_LDADD = $(top_builddir)/src/libpqxx.la ${POSTGRES_LIB} + +TESTS = runner +check_PROGRAMS = ${TESTS} diff --git a/ext/libpqxx-7.7.3/test/Makefile.am.template b/ext/libpqxx-7.7.3/test/Makefile.am.template new file mode 100644 index 000000000..2a21f6ef7 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/Makefile.am.template @@ -0,0 +1,38 @@ +# Makefile.am is generated automatically for automake whenever test programs are +# added to libpqxx. + +EXTRA_DIST = Makefile.am.template + +# Use the serial test runner, so tests don't get run in parallel. Otherwise, +# output from test failures will be hidden away in log files where we can't +# see them when running in a build slave. +AUTOMAKE_OPTIONS=serial-tests + + +AM_CPPFLAGS=-I$(top_builddir)/include -I$(top_srcdir)/include + +# Override automatically generated list of default includes. It contains only +# unnecessary entries, and incorrectly mentions include/pqxx directly. +DEFAULT_INCLUDES= + +noinst_HEADERS = test_helpers.hxx + +CLEANFILES=pqxxlo.txt +MAINTAINERCLEANFILES=Makefile.in + +#TESTS_ENVIRONMENT=PGDATABASE=libpqxx +# PGDATABASE, PGHOST, PGPORT, PGUSER + +runner_SOURCES = \ +###MAKTEMPLATE:FOREACH test/test*.cxx + ###BASENAME###.cxx \ +###MAKTEMPLATE:ENDFOREACH +###MAKTEMPLATE:FOREACH test/unit/test_*.cxx + unit/###BASENAME###.cxx \ +###MAKTEMPLATE:ENDFOREACH + runner.cxx + +runner_LDADD = $(top_builddir)/src/libpqxx.la ${POSTGRES_LIB} + +TESTS = runner +check_PROGRAMS = ${TESTS} diff --git a/ext/libpqxx-7.7.3/test/Makefile.in b/ext/libpqxx-7.7.3/test/Makefile.in new file mode 100644 index 000000000..8ffcca06c --- /dev/null +++ b/ext/libpqxx-7.7.3/test/Makefile.in @@ -0,0 +1,1302 @@ +# Makefile.in generated by automake 1.16.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# ############################################################################## +# AUTOMATICALLY GENERATED FILE -- DO NOT EDIT. +# +# This file is generated automatically by libpqxx's template2mak.py script, and +# will be rewritten from time to time. +# +# If you modify this file, chances are your modifications will be lost. +# +# The template2mak.py script should be available in the tools directory of the +# libpqxx source archive. +# +# Generated from template './test/Makefile.am.template'. +# ############################################################################## +# Makefile.am is generated automatically for automake whenever test programs are +# added to libpqxx. + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +TESTS = runner$(EXEEXT) +check_PROGRAMS = $(am__EXEEXT_1) +subdir = test +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/config/m4/libtool.m4 \ + $(top_srcdir)/config/m4/ltoptions.m4 \ + $(top_srcdir)/config/m4/ltsugar.m4 \ + $(top_srcdir)/config/m4/ltversion.m4 \ + $(top_srcdir)/config/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/include/pqxx/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__EXEEXT_1 = runner$(EXEEXT) +am__dirstamp = $(am__leading_dot)dirstamp +am_runner_OBJECTS = test00.$(OBJEXT) test01.$(OBJEXT) test02.$(OBJEXT) \ + test04.$(OBJEXT) test07.$(OBJEXT) test10.$(OBJEXT) \ + test11.$(OBJEXT) test13.$(OBJEXT) test14.$(OBJEXT) \ + test16.$(OBJEXT) test17.$(OBJEXT) test18.$(OBJEXT) \ + test20.$(OBJEXT) test21.$(OBJEXT) test26.$(OBJEXT) \ + test29.$(OBJEXT) test30.$(OBJEXT) test32.$(OBJEXT) \ + test37.$(OBJEXT) test39.$(OBJEXT) test46.$(OBJEXT) \ + test56.$(OBJEXT) test60.$(OBJEXT) test61.$(OBJEXT) \ + test62.$(OBJEXT) test69.$(OBJEXT) test70.$(OBJEXT) \ + test71.$(OBJEXT) test72.$(OBJEXT) test74.$(OBJEXT) \ + test75.$(OBJEXT) test76.$(OBJEXT) test77.$(OBJEXT) \ + test78.$(OBJEXT) test79.$(OBJEXT) test82.$(OBJEXT) \ + test84.$(OBJEXT) test87.$(OBJEXT) test88.$(OBJEXT) \ + test89.$(OBJEXT) test90.$(OBJEXT) unit/test_array.$(OBJEXT) \ + unit/test_binarystring.$(OBJEXT) unit/test_blob.$(OBJEXT) \ + unit/test_cancel_query.$(OBJEXT) unit/test_column.$(OBJEXT) \ + unit/test_composite.$(OBJEXT) unit/test_connection.$(OBJEXT) \ + unit/test_cursor.$(OBJEXT) unit/test_encodings.$(OBJEXT) \ + unit/test_error_verbosity.$(OBJEXT) \ + unit/test_errorhandler.$(OBJEXT) unit/test_escape.$(OBJEXT) \ + unit/test_exceptions.$(OBJEXT) unit/test_field.$(OBJEXT) \ + unit/test_float.$(OBJEXT) unit/test_largeobject.$(OBJEXT) \ + unit/test_nonblocking_connect.$(OBJEXT) \ + unit/test_notification.$(OBJEXT) unit/test_pipeline.$(OBJEXT) \ + unit/test_prepared_statement.$(OBJEXT) \ + unit/test_range.$(OBJEXT) unit/test_read_transaction.$(OBJEXT) \ + unit/test_result_iteration.$(OBJEXT) \ + unit/test_result_slicing.$(OBJEXT) unit/test_row.$(OBJEXT) \ + unit/test_separated_list.$(OBJEXT) \ + unit/test_simultaneous_transactions.$(OBJEXT) \ + unit/test_sql_cursor.$(OBJEXT) \ + unit/test_stateless_cursor.$(OBJEXT) \ + unit/test_strconv.$(OBJEXT) unit/test_stream_from.$(OBJEXT) \ + unit/test_stream_to.$(OBJEXT) \ + unit/test_string_conversion.$(OBJEXT) \ + unit/test_subtransaction.$(OBJEXT) \ + unit/test_test_helpers.$(OBJEXT) \ + unit/test_thread_safety_model.$(OBJEXT) \ + unit/test_time.$(OBJEXT) unit/test_transaction.$(OBJEXT) \ + unit/test_transaction_base.$(OBJEXT) \ + unit/test_transaction_focus.$(OBJEXT) \ + unit/test_transactor.$(OBJEXT) unit/test_type_name.$(OBJEXT) \ + unit/test_zview.$(OBJEXT) runner.$(OBJEXT) +runner_OBJECTS = $(am_runner_OBJECTS) +runner_DEPENDENCIES = $(top_builddir)/src/libpqxx.la +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/runner.Po ./$(DEPDIR)/test00.Po \ + ./$(DEPDIR)/test01.Po ./$(DEPDIR)/test02.Po \ + ./$(DEPDIR)/test04.Po ./$(DEPDIR)/test07.Po \ + ./$(DEPDIR)/test10.Po ./$(DEPDIR)/test11.Po \ + ./$(DEPDIR)/test13.Po ./$(DEPDIR)/test14.Po \ + ./$(DEPDIR)/test16.Po ./$(DEPDIR)/test17.Po \ + ./$(DEPDIR)/test18.Po ./$(DEPDIR)/test20.Po \ + ./$(DEPDIR)/test21.Po ./$(DEPDIR)/test26.Po \ + ./$(DEPDIR)/test29.Po ./$(DEPDIR)/test30.Po \ + ./$(DEPDIR)/test32.Po ./$(DEPDIR)/test37.Po \ + ./$(DEPDIR)/test39.Po ./$(DEPDIR)/test46.Po \ + ./$(DEPDIR)/test56.Po ./$(DEPDIR)/test60.Po \ + ./$(DEPDIR)/test61.Po ./$(DEPDIR)/test62.Po \ + ./$(DEPDIR)/test69.Po ./$(DEPDIR)/test70.Po \ + ./$(DEPDIR)/test71.Po ./$(DEPDIR)/test72.Po \ + ./$(DEPDIR)/test74.Po ./$(DEPDIR)/test75.Po \ + ./$(DEPDIR)/test76.Po ./$(DEPDIR)/test77.Po \ + ./$(DEPDIR)/test78.Po ./$(DEPDIR)/test79.Po \ + ./$(DEPDIR)/test82.Po ./$(DEPDIR)/test84.Po \ + ./$(DEPDIR)/test87.Po ./$(DEPDIR)/test88.Po \ + ./$(DEPDIR)/test89.Po ./$(DEPDIR)/test90.Po \ + unit/$(DEPDIR)/test_array.Po \ + unit/$(DEPDIR)/test_binarystring.Po \ + unit/$(DEPDIR)/test_blob.Po \ + unit/$(DEPDIR)/test_cancel_query.Po \ + unit/$(DEPDIR)/test_column.Po unit/$(DEPDIR)/test_composite.Po \ + unit/$(DEPDIR)/test_connection.Po \ + unit/$(DEPDIR)/test_cursor.Po unit/$(DEPDIR)/test_encodings.Po \ + unit/$(DEPDIR)/test_error_verbosity.Po \ + unit/$(DEPDIR)/test_errorhandler.Po \ + unit/$(DEPDIR)/test_escape.Po \ + unit/$(DEPDIR)/test_exceptions.Po unit/$(DEPDIR)/test_field.Po \ + unit/$(DEPDIR)/test_float.Po \ + unit/$(DEPDIR)/test_largeobject.Po \ + unit/$(DEPDIR)/test_nonblocking_connect.Po \ + unit/$(DEPDIR)/test_notification.Po \ + unit/$(DEPDIR)/test_pipeline.Po \ + unit/$(DEPDIR)/test_prepared_statement.Po \ + unit/$(DEPDIR)/test_range.Po \ + unit/$(DEPDIR)/test_read_transaction.Po \ + unit/$(DEPDIR)/test_result_iteration.Po \ + unit/$(DEPDIR)/test_result_slicing.Po \ + unit/$(DEPDIR)/test_row.Po \ + unit/$(DEPDIR)/test_separated_list.Po \ + unit/$(DEPDIR)/test_simultaneous_transactions.Po \ + unit/$(DEPDIR)/test_sql_cursor.Po \ + unit/$(DEPDIR)/test_stateless_cursor.Po \ + unit/$(DEPDIR)/test_strconv.Po \ + unit/$(DEPDIR)/test_stream_from.Po \ + unit/$(DEPDIR)/test_stream_to.Po \ + unit/$(DEPDIR)/test_string_conversion.Po \ + unit/$(DEPDIR)/test_subtransaction.Po \ + unit/$(DEPDIR)/test_test_helpers.Po \ + unit/$(DEPDIR)/test_thread_safety_model.Po \ + unit/$(DEPDIR)/test_time.Po unit/$(DEPDIR)/test_transaction.Po \ + unit/$(DEPDIR)/test_transaction_base.Po \ + unit/$(DEPDIR)/test_transaction_focus.Po \ + unit/$(DEPDIR)/test_transactor.Po \ + unit/$(DEPDIR)/test_type_name.Po unit/$(DEPDIR)/test_zview.Po +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(runner_SOURCES) +DIST_SOURCES = $(runner_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/config/depcomp \ + $(top_srcdir)/config/mkinstalldirs +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PG_CONFIG = @PG_CONFIG@ +PKG_CONFIG = @PKG_CONFIG@ +POSTGRES_INCLUDE = @POSTGRES_INCLUDE@ +PQXXVERSION = @PQXXVERSION@ +PQXX_ABI = @PQXX_ABI@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_postgres_lib = @with_postgres_lib@ +EXTRA_DIST = Makefile.am.template + +# Use the serial test runner, so tests don't get run in parallel. Otherwise, +# output from test failures will be hidden away in log files where we can't +# see them when running in a build slave. +AUTOMAKE_OPTIONS = serial-tests +AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include + +# Override automatically generated list of default includes. It contains only +# unnecessary entries, and incorrectly mentions include/pqxx directly. +DEFAULT_INCLUDES = +noinst_HEADERS = test_helpers.hxx +CLEANFILES = pqxxlo.txt +MAINTAINERCLEANFILES = Makefile.in + +#TESTS_ENVIRONMENT=PGDATABASE=libpqxx +# PGDATABASE, PGHOST, PGPORT, PGUSER +runner_SOURCES = \ + test00.cxx \ + test01.cxx \ + test02.cxx \ + test04.cxx \ + test07.cxx \ + test10.cxx \ + test11.cxx \ + test13.cxx \ + test14.cxx \ + test16.cxx \ + test17.cxx \ + test18.cxx \ + test20.cxx \ + test21.cxx \ + test26.cxx \ + test29.cxx \ + test30.cxx \ + test32.cxx \ + test37.cxx \ + test39.cxx \ + test46.cxx \ + test56.cxx \ + test60.cxx \ + test61.cxx \ + test62.cxx \ + test69.cxx \ + test70.cxx \ + test71.cxx \ + test72.cxx \ + test74.cxx \ + test75.cxx \ + test76.cxx \ + test77.cxx \ + test78.cxx \ + test79.cxx \ + test82.cxx \ + test84.cxx \ + test87.cxx \ + test88.cxx \ + test89.cxx \ + test90.cxx \ + unit/test_array.cxx \ + unit/test_binarystring.cxx \ + unit/test_blob.cxx \ + unit/test_cancel_query.cxx \ + unit/test_column.cxx \ + unit/test_composite.cxx \ + unit/test_connection.cxx \ + unit/test_cursor.cxx \ + unit/test_encodings.cxx \ + unit/test_error_verbosity.cxx \ + unit/test_errorhandler.cxx \ + unit/test_escape.cxx \ + unit/test_exceptions.cxx \ + unit/test_field.cxx \ + unit/test_float.cxx \ + unit/test_largeobject.cxx \ + unit/test_nonblocking_connect.cxx \ + unit/test_notification.cxx \ + unit/test_pipeline.cxx \ + unit/test_prepared_statement.cxx \ + unit/test_range.cxx \ + unit/test_read_transaction.cxx \ + unit/test_result_iteration.cxx \ + unit/test_result_slicing.cxx \ + unit/test_row.cxx \ + unit/test_separated_list.cxx \ + unit/test_simultaneous_transactions.cxx \ + unit/test_sql_cursor.cxx \ + unit/test_stateless_cursor.cxx \ + unit/test_strconv.cxx \ + unit/test_stream_from.cxx \ + unit/test_stream_to.cxx \ + unit/test_string_conversion.cxx \ + unit/test_subtransaction.cxx \ + unit/test_test_helpers.cxx \ + unit/test_thread_safety_model.cxx \ + unit/test_time.cxx \ + unit/test_transaction.cxx \ + unit/test_transaction_base.cxx \ + unit/test_transaction_focus.cxx \ + unit/test_transactor.cxx \ + unit/test_type_name.cxx \ + unit/test_zview.cxx \ + runner.cxx + +runner_LDADD = $(top_builddir)/src/libpqxx.la ${POSTGRES_LIB} +all: all-am + +.SUFFIXES: +.SUFFIXES: .cxx .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu test/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu test/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +unit/$(am__dirstamp): + @$(MKDIR_P) unit + @: > unit/$(am__dirstamp) +unit/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) unit/$(DEPDIR) + @: > unit/$(DEPDIR)/$(am__dirstamp) +unit/test_array.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_binarystring.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_blob.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_cancel_query.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_column.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_composite.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_connection.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_cursor.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_encodings.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_error_verbosity.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_errorhandler.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_escape.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_exceptions.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_field.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_float.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_largeobject.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_nonblocking_connect.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_notification.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_pipeline.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_prepared_statement.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_range.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_read_transaction.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_result_iteration.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_result_slicing.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_row.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_separated_list.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_simultaneous_transactions.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_sql_cursor.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_stateless_cursor.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_strconv.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_stream_from.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_stream_to.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_string_conversion.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_subtransaction.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_test_helpers.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_thread_safety_model.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_time.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_transaction.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_transaction_base.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_transaction_focus.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_transactor.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_type_name.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test_zview.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) + +runner$(EXEEXT): $(runner_OBJECTS) $(runner_DEPENDENCIES) $(EXTRA_runner_DEPENDENCIES) + @rm -f runner$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(runner_OBJECTS) $(runner_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f unit/*.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/runner.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test00.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test01.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test02.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test04.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test07.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test10.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test11.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test13.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test14.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test16.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test17.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test18.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test20.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test21.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test26.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test29.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test30.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test37.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test39.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test46.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test56.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test60.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test61.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test62.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test69.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test70.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test71.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test72.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test74.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test75.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test76.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test77.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test78.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test79.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test82.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test84.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test87.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test88.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test89.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test90.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_array.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_binarystring.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_blob.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_cancel_query.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_column.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_composite.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_connection.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_cursor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_encodings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_error_verbosity.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_errorhandler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_escape.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_exceptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_field.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_float.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_largeobject.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_nonblocking_connect.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_notification.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_pipeline.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_prepared_statement.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_range.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_read_transaction.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_result_iteration.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_result_slicing.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_row.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_separated_list.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_simultaneous_transactions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_sql_cursor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_stateless_cursor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_strconv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_stream_from.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_stream_to.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_string_conversion.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_subtransaction.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_test_helpers.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_thread_safety_model.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_time.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_transaction.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_transaction_base.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_transaction_focus.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_transactor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_type_name.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_zview.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cxx.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cxx.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cxx.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(HEADERS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f unit/$(DEPDIR)/$(am__dirstamp) + -rm -f unit/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/runner.Po + -rm -f ./$(DEPDIR)/test00.Po + -rm -f ./$(DEPDIR)/test01.Po + -rm -f ./$(DEPDIR)/test02.Po + -rm -f ./$(DEPDIR)/test04.Po + -rm -f ./$(DEPDIR)/test07.Po + -rm -f ./$(DEPDIR)/test10.Po + -rm -f ./$(DEPDIR)/test11.Po + -rm -f ./$(DEPDIR)/test13.Po + -rm -f ./$(DEPDIR)/test14.Po + -rm -f ./$(DEPDIR)/test16.Po + -rm -f ./$(DEPDIR)/test17.Po + -rm -f ./$(DEPDIR)/test18.Po + -rm -f ./$(DEPDIR)/test20.Po + -rm -f ./$(DEPDIR)/test21.Po + -rm -f ./$(DEPDIR)/test26.Po + -rm -f ./$(DEPDIR)/test29.Po + -rm -f ./$(DEPDIR)/test30.Po + -rm -f ./$(DEPDIR)/test32.Po + -rm -f ./$(DEPDIR)/test37.Po + -rm -f ./$(DEPDIR)/test39.Po + -rm -f ./$(DEPDIR)/test46.Po + -rm -f ./$(DEPDIR)/test56.Po + -rm -f ./$(DEPDIR)/test60.Po + -rm -f ./$(DEPDIR)/test61.Po + -rm -f ./$(DEPDIR)/test62.Po + -rm -f ./$(DEPDIR)/test69.Po + -rm -f ./$(DEPDIR)/test70.Po + -rm -f ./$(DEPDIR)/test71.Po + -rm -f ./$(DEPDIR)/test72.Po + -rm -f ./$(DEPDIR)/test74.Po + -rm -f ./$(DEPDIR)/test75.Po + -rm -f ./$(DEPDIR)/test76.Po + -rm -f ./$(DEPDIR)/test77.Po + -rm -f ./$(DEPDIR)/test78.Po + -rm -f ./$(DEPDIR)/test79.Po + -rm -f ./$(DEPDIR)/test82.Po + -rm -f ./$(DEPDIR)/test84.Po + -rm -f ./$(DEPDIR)/test87.Po + -rm -f ./$(DEPDIR)/test88.Po + -rm -f ./$(DEPDIR)/test89.Po + -rm -f ./$(DEPDIR)/test90.Po + -rm -f unit/$(DEPDIR)/test_array.Po + -rm -f unit/$(DEPDIR)/test_binarystring.Po + -rm -f unit/$(DEPDIR)/test_blob.Po + -rm -f unit/$(DEPDIR)/test_cancel_query.Po + -rm -f unit/$(DEPDIR)/test_column.Po + -rm -f unit/$(DEPDIR)/test_composite.Po + -rm -f unit/$(DEPDIR)/test_connection.Po + -rm -f unit/$(DEPDIR)/test_cursor.Po + -rm -f unit/$(DEPDIR)/test_encodings.Po + -rm -f unit/$(DEPDIR)/test_error_verbosity.Po + -rm -f unit/$(DEPDIR)/test_errorhandler.Po + -rm -f unit/$(DEPDIR)/test_escape.Po + -rm -f unit/$(DEPDIR)/test_exceptions.Po + -rm -f unit/$(DEPDIR)/test_field.Po + -rm -f unit/$(DEPDIR)/test_float.Po + -rm -f unit/$(DEPDIR)/test_largeobject.Po + -rm -f unit/$(DEPDIR)/test_nonblocking_connect.Po + -rm -f unit/$(DEPDIR)/test_notification.Po + -rm -f unit/$(DEPDIR)/test_pipeline.Po + -rm -f unit/$(DEPDIR)/test_prepared_statement.Po + -rm -f unit/$(DEPDIR)/test_range.Po + -rm -f unit/$(DEPDIR)/test_read_transaction.Po + -rm -f unit/$(DEPDIR)/test_result_iteration.Po + -rm -f unit/$(DEPDIR)/test_result_slicing.Po + -rm -f unit/$(DEPDIR)/test_row.Po + -rm -f unit/$(DEPDIR)/test_separated_list.Po + -rm -f unit/$(DEPDIR)/test_simultaneous_transactions.Po + -rm -f unit/$(DEPDIR)/test_sql_cursor.Po + -rm -f unit/$(DEPDIR)/test_stateless_cursor.Po + -rm -f unit/$(DEPDIR)/test_strconv.Po + -rm -f unit/$(DEPDIR)/test_stream_from.Po + -rm -f unit/$(DEPDIR)/test_stream_to.Po + -rm -f unit/$(DEPDIR)/test_string_conversion.Po + -rm -f unit/$(DEPDIR)/test_subtransaction.Po + -rm -f unit/$(DEPDIR)/test_test_helpers.Po + -rm -f unit/$(DEPDIR)/test_thread_safety_model.Po + -rm -f unit/$(DEPDIR)/test_time.Po + -rm -f unit/$(DEPDIR)/test_transaction.Po + -rm -f unit/$(DEPDIR)/test_transaction_base.Po + -rm -f unit/$(DEPDIR)/test_transaction_focus.Po + -rm -f unit/$(DEPDIR)/test_transactor.Po + -rm -f unit/$(DEPDIR)/test_type_name.Po + -rm -f unit/$(DEPDIR)/test_zview.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/runner.Po + -rm -f ./$(DEPDIR)/test00.Po + -rm -f ./$(DEPDIR)/test01.Po + -rm -f ./$(DEPDIR)/test02.Po + -rm -f ./$(DEPDIR)/test04.Po + -rm -f ./$(DEPDIR)/test07.Po + -rm -f ./$(DEPDIR)/test10.Po + -rm -f ./$(DEPDIR)/test11.Po + -rm -f ./$(DEPDIR)/test13.Po + -rm -f ./$(DEPDIR)/test14.Po + -rm -f ./$(DEPDIR)/test16.Po + -rm -f ./$(DEPDIR)/test17.Po + -rm -f ./$(DEPDIR)/test18.Po + -rm -f ./$(DEPDIR)/test20.Po + -rm -f ./$(DEPDIR)/test21.Po + -rm -f ./$(DEPDIR)/test26.Po + -rm -f ./$(DEPDIR)/test29.Po + -rm -f ./$(DEPDIR)/test30.Po + -rm -f ./$(DEPDIR)/test32.Po + -rm -f ./$(DEPDIR)/test37.Po + -rm -f ./$(DEPDIR)/test39.Po + -rm -f ./$(DEPDIR)/test46.Po + -rm -f ./$(DEPDIR)/test56.Po + -rm -f ./$(DEPDIR)/test60.Po + -rm -f ./$(DEPDIR)/test61.Po + -rm -f ./$(DEPDIR)/test62.Po + -rm -f ./$(DEPDIR)/test69.Po + -rm -f ./$(DEPDIR)/test70.Po + -rm -f ./$(DEPDIR)/test71.Po + -rm -f ./$(DEPDIR)/test72.Po + -rm -f ./$(DEPDIR)/test74.Po + -rm -f ./$(DEPDIR)/test75.Po + -rm -f ./$(DEPDIR)/test76.Po + -rm -f ./$(DEPDIR)/test77.Po + -rm -f ./$(DEPDIR)/test78.Po + -rm -f ./$(DEPDIR)/test79.Po + -rm -f ./$(DEPDIR)/test82.Po + -rm -f ./$(DEPDIR)/test84.Po + -rm -f ./$(DEPDIR)/test87.Po + -rm -f ./$(DEPDIR)/test88.Po + -rm -f ./$(DEPDIR)/test89.Po + -rm -f ./$(DEPDIR)/test90.Po + -rm -f unit/$(DEPDIR)/test_array.Po + -rm -f unit/$(DEPDIR)/test_binarystring.Po + -rm -f unit/$(DEPDIR)/test_blob.Po + -rm -f unit/$(DEPDIR)/test_cancel_query.Po + -rm -f unit/$(DEPDIR)/test_column.Po + -rm -f unit/$(DEPDIR)/test_composite.Po + -rm -f unit/$(DEPDIR)/test_connection.Po + -rm -f unit/$(DEPDIR)/test_cursor.Po + -rm -f unit/$(DEPDIR)/test_encodings.Po + -rm -f unit/$(DEPDIR)/test_error_verbosity.Po + -rm -f unit/$(DEPDIR)/test_errorhandler.Po + -rm -f unit/$(DEPDIR)/test_escape.Po + -rm -f unit/$(DEPDIR)/test_exceptions.Po + -rm -f unit/$(DEPDIR)/test_field.Po + -rm -f unit/$(DEPDIR)/test_float.Po + -rm -f unit/$(DEPDIR)/test_largeobject.Po + -rm -f unit/$(DEPDIR)/test_nonblocking_connect.Po + -rm -f unit/$(DEPDIR)/test_notification.Po + -rm -f unit/$(DEPDIR)/test_pipeline.Po + -rm -f unit/$(DEPDIR)/test_prepared_statement.Po + -rm -f unit/$(DEPDIR)/test_range.Po + -rm -f unit/$(DEPDIR)/test_read_transaction.Po + -rm -f unit/$(DEPDIR)/test_result_iteration.Po + -rm -f unit/$(DEPDIR)/test_result_slicing.Po + -rm -f unit/$(DEPDIR)/test_row.Po + -rm -f unit/$(DEPDIR)/test_separated_list.Po + -rm -f unit/$(DEPDIR)/test_simultaneous_transactions.Po + -rm -f unit/$(DEPDIR)/test_sql_cursor.Po + -rm -f unit/$(DEPDIR)/test_stateless_cursor.Po + -rm -f unit/$(DEPDIR)/test_strconv.Po + -rm -f unit/$(DEPDIR)/test_stream_from.Po + -rm -f unit/$(DEPDIR)/test_stream_to.Po + -rm -f unit/$(DEPDIR)/test_string_conversion.Po + -rm -f unit/$(DEPDIR)/test_subtransaction.Po + -rm -f unit/$(DEPDIR)/test_test_helpers.Po + -rm -f unit/$(DEPDIR)/test_thread_safety_model.Po + -rm -f unit/$(DEPDIR)/test_time.Po + -rm -f unit/$(DEPDIR)/test_transaction.Po + -rm -f unit/$(DEPDIR)/test_transaction_base.Po + -rm -f unit/$(DEPDIR)/test_transaction_focus.Po + -rm -f unit/$(DEPDIR)/test_transactor.Po + -rm -f unit/$(DEPDIR)/test_type_name.Po + -rm -f unit/$(DEPDIR)/test_zview.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ + check-am clean clean-checkPROGRAMS clean-generic clean-libtool \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ext/libpqxx-7.7.3/test/runner.cxx b/ext/libpqxx-7.7.3/test/runner.cxx new file mode 100644 index 000000000..38c9849d8 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/runner.cxx @@ -0,0 +1,203 @@ +/* libpqxx test runner. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "test_helpers.hxx" + +namespace +{ +inline std::string deref_field(pqxx::field const &f) +{ + return f.c_str(); +} +} // namespace + + +namespace pqxx::test +{ +test_failure::test_failure( + std::string const &ffile, int fline, std::string const &desc) : + std::logic_error(desc), m_file(ffile), m_line(fline) +{} + +test_failure::~test_failure() noexcept = default; + + +/// Drop table, if it exists. +inline void drop_table(transaction_base &t, std::string const &table) +{ + t.exec("DROP TABLE IF EXISTS " + table); +} + + +[[noreturn]] void +check_notreached(char const file[], int line, std::string desc) +{ + throw test_failure(file, line, desc); +} + + +void check( + char const file[], int line, bool condition, char const text[], + std::string const &desc) +{ + if (not condition) + throw test_failure( + file, line, desc + " (failed expression: " + text + ")"); +} + + +void expected_exception(std::string const &message) +{ + std::cout << "(Expected) " << message << std::endl; +} + + +std::string list_row(row Obj) +{ + return separated_list(", ", std::begin(Obj), std::end(Obj), deref_field); +} + + +std::string list_result(result Obj) +{ + if (std::empty(Obj)) + return ""; + return "{" + + separated_list( + "}\n{", std::begin(Obj), std::end(Obj), + [](row r) { return list_row(r); }) + + "}"; +} + + +std::string list_result_iterator(result::const_iterator Obj) +{ + return ""; +} + + +void create_pqxxevents(transaction_base &t) +{ + t.exec( + "CREATE TEMP TABLE pqxxevents(year integer, event varchar) " + "ON COMMIT PRESERVE ROWS"); + t.exec("INSERT INTO pqxxevents(year, event) VALUES (71, 'jtv')"); + t.exec("INSERT INTO pqxxevents(year, event) VALUES (38, 'time_t overflow')"); + t.exec( + "INSERT INTO pqxxevents(year, event) VALUES (1, '''911'' WTC attack')"); + t.exec("INSERT INTO pqxxevents(year, event) VALUES (81, 'C:\\>')"); + t.exec( + "INSERT INTO pqxxevents(year, event) VALUES (1978, 'bloody\t\tcold')"); + t.exec("INSERT INTO pqxxevents(year, event) VALUES (99, '')"); + t.exec("INSERT INTO pqxxevents(year, event) VALUES (2002, 'libpqxx')"); + t.exec( + "INSERT INTO pqxxevents(year, event) " + "VALUES (1989, 'Ode an die Freiheit')"); + t.exec( + "INSERT INTO pqxxevents(year, event) VALUES (2001, 'New millennium')"); + t.exec("INSERT INTO pqxxevents(year, event) VALUES (1974, '')"); + t.exec("INSERT INTO pqxxevents(year, event) VALUES (97, 'Asian crisis')"); + t.exec( + "INSERT INTO pqxxevents(year, event) VALUES (2001, 'A Space Odyssey')"); +} +} // namespace pqxx::test + + +namespace +{ +std::map *all_tests{nullptr}; +} // namespace + + +namespace pqxx::test +{ +void register_test(char const name[], pqxx::test::testfunc func) +{ + if (all_tests == nullptr) + { + all_tests = new std::map(); + } + else + { + assert(all_tests->find(name) == all_tests->end()); + } + (*all_tests)[name] = func; +} +} // namespace pqxx::test + + +int main(int argc, char const *argv[]) +{ + char const *const test_name{(argc > 1) ? argv[1] : nullptr}; + + int test_count = 0; + std::list failed; + for (auto const &i : *all_tests) + if (test_name == nullptr or std::string{test_name} == std::string{i.first}) + { + std::cout << std::endl << "Running: " << i.first << std::endl; + + bool success = false; + try + { + i.second(); + success = true; + } + catch (pqxx::test::test_failure const &e) + { + std::cerr << "Test failure in " + e.file() + " line " + + pqxx::to_string(e.line()) + << ": " << e.what() << std::endl; + } + catch (std::bad_alloc const &) + { + std::cerr << "Out of memory!" << std::endl; + } + catch (pqxx::feature_not_supported const &e) + { + std::cerr << "Not testing unsupported feature: " << e.what() + << std::endl; + success = true; + --test_count; + } + catch (pqxx::sql_error const &e) + { + std::cerr << "SQL error: " << e.what() << std::endl + << "Query was: " << e.query() << std::endl; + } + catch (std::exception const &e) + { + std::cerr << "Exception: " << e.what() << std::endl; + } + catch (...) + { + std::cerr << "Unknown exception" << std::endl; + } + + if (not success) + { + std::cerr << "FAILED: " << i.first << std::endl; + failed.emplace_back(i.first); + } + ++test_count; + } + + std::cout << "Ran " << test_count << " test(s).\n"; + + if (not std::empty(failed)) + { + std::cerr << "*** " << std::size(failed) << " test(s) failed: ***\n"; + for (auto const &i : failed) std::cerr << "\t" << i << '\n'; + } + + return int(std::size(failed)); +} diff --git a/ext/libpqxx-7.7.3/test/test00.cxx b/ext/libpqxx-7.7.3/test/test00.cxx new file mode 100644 index 000000000..698c9b81b --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test00.cxx @@ -0,0 +1,114 @@ +#include + +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Initial test program for libpqxx. Test functionality that doesn't require a +// running database. + +namespace +{ +template +inline void +strconv(std::string const &type, T const &Obj, std::string const &expected) +{ + std::string const Objstr{to_string(Obj)}; + + PQXX_CHECK_EQUAL(Objstr, expected, "String mismatch for " + type + "."); + T NewObj; + from_string(Objstr, NewObj); + PQXX_CHECK_EQUAL( + to_string(NewObj), expected, "String mismatch for recycled " + type + "."); +} + +// There's no from_string()... +inline void +strconv(std::string const &type, char const Obj[], std::string const &expected) +{ + std::string const Objstr(to_string(Obj)); + PQXX_CHECK_EQUAL(Objstr, expected, "String mismatch for " + type + "."); +} + +constexpr double not_a_number{std::numeric_limits::quiet_NaN()}; + +struct intderef +{ + template int operator()(ITER i) const noexcept + { + return int(*i); + } +}; + + +void test_000() +{ + PQXX_CHECK_EQUAL( + oid_none, 0u, + "InvalidIod is not zero as it used to be. This may conceivably " + "cause problems in libpqxx."); + + PQXX_CHECK( + cursor_base::prior() < 0 and cursor_base::backward_all() < 0, + "cursor_base::difference_type appears to be unsigned."); + + constexpr char weird[]{"foo\t\n\0bar"}; + std::string const weirdstr(weird, std::size(weird) - 1); + + // Test string conversions + strconv("char const[]", "", ""); + strconv("char const[]", "foo", "foo"); + strconv("int", 0, "0"); + strconv("int", 100, "100"); + strconv("int", -1, "-1"); + +#if defined(_MSC_VER) + long const long_min{LONG_MIN}, long_max{LONG_MAX}; +#else + long const long_min{std::numeric_limits::min()}, + long_max{std::numeric_limits::max()}; +#endif + + std::stringstream lminstr, lmaxstr, llminstr, llmaxstr, ullmaxstr; + lminstr.imbue(std::locale("C")); + lmaxstr.imbue(std::locale("C")); + llminstr.imbue(std::locale("C")); + llmaxstr.imbue(std::locale("C")); + ullmaxstr.imbue(std::locale("C")); + + lminstr << long_min; + lmaxstr << long_max; + + auto const ullong_max{std::numeric_limits::max()}; + auto const llong_max{std::numeric_limits::max()}, + llong_min{std::numeric_limits::min()}; + + llminstr << llong_min; + llmaxstr << llong_max; + ullmaxstr << ullong_max; + + strconv("long", 0, "0"); + strconv("long", long_min, lminstr.str()); + strconv("long", long_max, lmaxstr.str()); + strconv("double", not_a_number, "nan"); + strconv("string", std::string{}, ""); + strconv("string", weirdstr, weirdstr); + strconv("long long", 0LL, "0"); + strconv("long long", llong_min, llminstr.str()); + strconv("long long", llong_max, llmaxstr.str()); + strconv("unsigned long long", 0ULL, "0"); + strconv("unsigned long long", ullong_max, ullmaxstr.str()); + + std::stringstream ss; + strconv("empty stringstream", ss, ""); + ss << -3.1415; + strconv("stringstream", ss, ss.str()); +} + + +PQXX_REGISTER_TEST(test_000); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test01.cxx b/ext/libpqxx-7.7.3/test/test01.cxx new file mode 100644 index 000000000..84b7f3b78 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test01.cxx @@ -0,0 +1,33 @@ +#include + +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +namespace +{ +// Simple test program for libpqxx. Open connection to database, start +// a transaction, and perform a query inside it. +void test_001() +{ + connection conn; + + // Begin a transaction acting on our current connection. Give it a human- + // readable name so the library can include it in error messages. + work tx{conn, "test1"}; + + // Perform a query on the database, storing result rows in R. + result r(tx.exec("SELECT * FROM pg_tables")); + + // We're expecting to find some tables... + PQXX_CHECK(not std::empty(r), "No tables found."); + + tx.commit(); +} + + +PQXX_REGISTER_TEST(test_001); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test02.cxx b/ext/libpqxx-7.7.3/test/test02.cxx new file mode 100644 index 000000000..f98e18d08 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test02.cxx @@ -0,0 +1,76 @@ +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Example/test program for libpqxx. Perform a query and enumerate its output +// using array indexing. + +namespace +{ +void bad_connect() +{ + connection conn{"totally#invalid@connect$string!?"}; +} + +void test_002() +{ + // Before we really connect, test the expected behaviour of the default + // connection type, where a failure to connect results in an immediate + // exception rather than a silent retry. + PQXX_CHECK_THROWS_EXCEPTION( + bad_connect(), "Invalid connection string did not cause exception."); + + // Set up connection to database + std::string ConnectString; + connection C{ConnectString}; + + // Start transaction within context of connection. + work T{C, "test2"}; + + // Perform query within transaction. + result R(T.exec("SELECT * FROM pg_tables")); + + // Let's keep the database waiting as briefly as possible: commit now, + // before we start processing results. We could do this later, or since + // we're not making any changes in the database that need to be committed, + // we could in this case even omit it altogether. + T.commit(); + + // Ah, this version of postgres will tell you which table a column in a + // result came from. Let's just test that functionality... + oid const rtable{R.column_table(0)}; + PQXX_CHECK_EQUAL( + rtable, R.column_table(pqxx::row::size_type(0)), + "Inconsistent answers from column_table()"); + + std::string const rcol{R.column_name(0)}; + oid const crtable{R.column_table(rcol)}; + PQXX_CHECK_EQUAL( + crtable, rtable, "Field looked up by name gives different origin."); + + // Now we've got all that settled, let's process our results. + for (auto const &f : R) + { + oid const ftable{f[0].table()}; + PQXX_CHECK_EQUAL(ftable, rtable, "field::table() is broken."); + + oid const ttable{f.column_table(0)}; + + PQXX_CHECK_EQUAL( + ttable, f.column_table(pqxx::row::size_type(0)), + "Inconsistent pqxx::row::column_table()."); + + PQXX_CHECK_EQUAL(ttable, rtable, "Inconsistent result::column_table()."); + + oid const cttable{f.column_table(rcol)}; + + PQXX_CHECK_EQUAL(cttable, rtable, "pqxx::row::column_table() is broken."); + } +} + + +PQXX_REGISTER_TEST(test_002); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test04.cxx b/ext/libpqxx-7.7.3/test/test04.cxx new file mode 100644 index 000000000..623446398 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test04.cxx @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + +// Example program for libpqxx. Send notification to self. + +namespace +{ +int Backend_PID{0}; + + +// Sample implementation of notification receiver. +class TestListener final : public notification_receiver +{ + bool m_done; + +public: + explicit TestListener(connection &conn) : + notification_receiver(conn, "listen"), m_done(false) + {} + + virtual void operator()(std::string const &, int be_pid) override + { + m_done = true; + PQXX_CHECK_EQUAL( + be_pid, Backend_PID, "Notification came from wrong backend process."); + } + + bool done() const { return m_done; } +}; + + +void test_004() +{ + connection conn; + + TestListener L{conn}; + // Trigger our notification receiver. + perform([&conn, &L] { + work tx(conn); + tx.exec0("NOTIFY " + conn.quote_name(L.channel())); + Backend_PID = conn.backendpid(); + tx.commit(); + }); + + int notifs{0}; + for (int i{0}; (i < 20) and not L.done(); ++i) + { + PQXX_CHECK_EQUAL(notifs, 0, "Got unexpected notifications."); + // Sleep for one second. I'm not proud of this, but how does one inject + // a change to the built-in clock in a static language? + pqxx::internal::wait_for(1'000'000u); + notifs = conn.get_notifs(); + } + + PQXX_CHECK_NOT_EQUAL(L.done(), false, "No notification received."); + PQXX_CHECK_EQUAL(notifs, 1, "Got too many notifications."); +} + + +PQXX_REGISTER_TEST(test_004); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test07.cxx b/ext/libpqxx-7.7.3/test/test07.cxx new file mode 100644 index 000000000..875ee8d50 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test07.cxx @@ -0,0 +1,133 @@ +#include +#include + +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Example program for libpqxx. Modify the database, retaining transactional +// integrity using the transactor framework. +// +// This assumes the existence of a database table "pqxxevents" containing a +// 2-digit "year" field, which is extended to a 4-digit format by assuming all +// year numbers of 70 or higher are in the 20th century, and all others in the +// 21st, and that no years before 1970 are possible. + +namespace +{ +// Convert year to 4-digit format. +int To4Digits(int Y) +{ + int Result{Y}; + + PQXX_CHECK(Y >= 0, "Negative year: " + to_string(Y)); + if (Y < 70) + Result += 2000; + else if (Y < 100) + Result += 1900; + else if (Y < 1970) + PQXX_CHECK_NOTREACHED("Unexpected year: " + to_string(Y)); + + return Result; +} + + +void test_007() +{ + connection conn; + conn.set_client_encoding("SQL_ASCII"); + + { + work tx{conn}; + test::create_pqxxevents(tx); + tx.commit(); + } + + // Perform (an instantiation of) the UpdateYears transactor we've defined + // in the code above. This is where the work gets done. + std::map conversions; + perform([&conversions, &conn] { + work tx{conn}; + // First select all different years occurring in the table. + result R(tx.exec("SELECT year FROM pqxxevents")); + + // See if we get reasonable type identifier for this column. + oid const rctype{R.column_type(0)}; + PQXX_CHECK_EQUAL( + R.column_type(pqxx::row::size_type(0)), rctype, + "Inconsistent result::column_type()."); + + std::string const rct{to_string(rctype)}; + PQXX_CHECK(rctype > 0, "Got strange type ID for column: " + rct); + + std::string const rcol{R.column_name(0)}; + PQXX_CHECK(not std::empty(rcol), "Didn't get a name for column."); + + oid const rcctype{R.column_type(rcol)}; + PQXX_CHECK_EQUAL( + rcctype, rctype, "Column type is not what it is by name."); + + oid const rawrcctype{R.column_type(rcol)}; + PQXX_CHECK_EQUAL( + rawrcctype, rctype, "Column type by C-style name is different."); + + // Note all different years currently occurring in the table, writing + // them and their correct mappings to conversions. + for (auto const &r : R) + { + int Y{0}; + + // Read year, and if it is non-null, note its converted value + if (r[0] >> Y) + conversions[Y] = To4Digits(Y); + + // See if type identifiers are consistent + oid const tctype{r.column_type(0)}; + + PQXX_CHECK_EQUAL( + tctype, r.column_type(pqxx::row::size_type(0)), + "Inconsistent pqxx::row::column_type()"); + + PQXX_CHECK_EQUAL( + tctype, rctype, + "pqxx::row::column_type() is inconsistent with " + "result::column_type()."); + + oid const ctctype{r.column_type(rcol)}; + + PQXX_CHECK_EQUAL( + ctctype, rctype, "Column type lookup by column name is broken."); + + oid const rawctctype{r.column_type(rcol)}; + + PQXX_CHECK_EQUAL( + rawctctype, rctype, "Column type lookup by C-style name is broken."); + + oid const fctype{r[0].type()}; + PQXX_CHECK_EQUAL(fctype, rctype, "Field type lookup is broken."); + } + + // For each occurring year, write converted date back to whereever it may + // occur in the table. Since we're in a transaction, any changes made by + // others at the same time will not affect us. + for (auto const &c : conversions) + { + auto const query{ + "UPDATE pqxxevents " + "SET year=" + + to_string(c.second) + + " " + "WHERE year=" + + to_string(c.first)}; + R = tx.exec0(query); + } + }); +} + + +PQXX_REGISTER_TEST(test_007); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test10.cxx b/ext/libpqxx-7.7.3/test/test10.cxx new file mode 100644 index 000000000..4cb4aaba1 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test10.cxx @@ -0,0 +1,106 @@ +#include +#include +#include + +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Open connection to database, start a transaction, +// abort it, and verify that it "never happened." + +namespace +{ +// Let's take a boring year that is not going to be in the "pqxxevents" table +constexpr int BoringYear{1977}; + +std::string const Table("pqxxevents"); + + +// Count events, and boring events, in table +std::pair CountEvents(transaction_base &T) +{ + std::string const events_query{"SELECT count(*) FROM " + Table}; + std::string const boring_query{ + events_query + " WHERE year=" + to_string(BoringYear)}; + return std::make_pair( + T.query_value(events_query), T.query_value(boring_query)); +} + + +// Try adding a record, then aborting it, and check whether the abort was +// performed correctly. +void Test(connection &C, bool ExplicitAbort) +{ + std::pair EventCounts; + + // First run our doomed transaction. This will refuse to run if an event + // exists for our Boring Year. + { + // Begin a transaction acting on our current connection; we'll abort it + // later though. + work Doomed{C, "Doomed"}; + + // Verify that our Boring Year was not yet in the events table + EventCounts = CountEvents(Doomed); + + PQXX_CHECK_EQUAL( + EventCounts.second, 0, "Can't run, boring year is already in table."); + + // Now let's try to introduce a row for our Boring Year + Doomed.exec0( + "INSERT INTO " + Table + + "(year, event) " + "VALUES (" + + to_string(BoringYear) + ", 'yawn')"); + + auto const Recount{CountEvents(Doomed)}; + PQXX_CHECK_EQUAL( + Recount.second, 1, "Wrong # events for " + to_string(BoringYear)); + + PQXX_CHECK_EQUAL( + Recount.first, EventCounts.first + 1, "Number of events changed."); + + // Okay, we've added an entry but we don't really want to. Abort it + // explicitly if requested, or simply let the Transaction object "expire." + if (ExplicitAbort) + Doomed.abort(); + + // If now explicit abort requested, Doomed Transaction still ends here + } + + // Now check that we're back in the original state. Note that this may go + // wrong if somebody managed to change the table between our two + // transactions. + work Checkup(C, "Checkup"); + + auto const NewEvents{CountEvents(Checkup)}; + PQXX_CHECK_EQUAL( + NewEvents.first, EventCounts.first, + "Number of events changed. This may be due to a bug in libpqxx, " + "or the test table was modified by some other process."); + + PQXX_CHECK_EQUAL( + NewEvents.second, 0, + "Found unexpected events. This may be due to a bug in libpqxx, " + "or the test table was modified by some other process."); +} + + +void test_abort() +{ + connection conn; + nontransaction t{conn}; + test::create_pqxxevents(t); + connection &c(t.conn()); + t.commit(); + Test(c, true); + Test(c, false); +} + + +PQXX_REGISTER_TEST(test_abort); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test11.cxx b/ext/libpqxx-7.7.3/test/test11.cxx new file mode 100644 index 000000000..4b42a66d5 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test11.cxx @@ -0,0 +1,76 @@ +#include +#include +#include + +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Query a table and report its metadata. +namespace +{ +void test_011() +{ + connection conn; + work tx{conn}; + std::string const Table{"pg_tables"}; + + result R(tx.exec("SELECT * FROM " + Table)); + + // Print column names + for (pqxx::row::size_type c{0}; c < R.columns(); ++c) + { + std::string N{R.column_name(c)}; + PQXX_CHECK_EQUAL(R.column_number(N), c, "Inconsistent column numbers."); + } + + // If there are rows in R, compare their metadata to R's. + if (not std::empty(R)) + { + PQXX_CHECK_EQUAL(R[0].rownumber(), 0, "Row 0 has wrong number."); + + if (std::size(R) >= 2) + PQXX_CHECK_EQUAL(R[1].rownumber(), 1, "Row 1 has wrong number."); + + // Test result::iterator::swap() + pqxx::result::const_iterator const T1(R[0]), T2(R[1]); + PQXX_CHECK_NOT_EQUAL(T1, T2, "Values are identical--can't test swap()."); + pqxx::result::const_iterator T1s(T1), T2s(T2); + PQXX_CHECK_EQUAL(T1s, T1, "Result iterator copy-construction is wrong."); + PQXX_CHECK_EQUAL( + T2s, T2, "Result iterator copy-construction is inconsistently wrong."); + T1s.swap(T2s); + PQXX_CHECK_NOT_EQUAL(T1s, T1, "Result iterator swap doesn't work."); + PQXX_CHECK_NOT_EQUAL( + T2s, T2, "Result iterator swap inconsistently wrong."); + PQXX_CHECK_EQUAL(T2s, T1, "Result iterator swap is asymmetric."); + PQXX_CHECK_EQUAL( + T1s, T2, "Result iterator swap is inconsistently asymmetric."); + + for (pqxx::row::size_type c{0}; c < std::size(R[0]); ++c) + { + std::string N{R.column_name(c)}; + + PQXX_CHECK_EQUAL( + std::string{R[0].at(c).c_str()}, R[0].at(N).c_str(), + "Field by name != field by number."); + + PQXX_CHECK_EQUAL( + std::string{R[0][c].c_str()}, R[0][N].c_str(), + "at() is inconsistent with operator[]."); + + PQXX_CHECK_EQUAL(R[0][c].name(), N, "Field names are inconsistent."); + + PQXX_CHECK_EQUAL( + std::size(R[0][c]), strlen(R[0][c].c_str()), + "Field size is not what we expected."); + } + } +} + + +PQXX_REGISTER_TEST(test_011); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test13.cxx b/ext/libpqxx-7.7.3/test/test13.cxx new file mode 100644 index 000000000..b3577a5d3 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test13.cxx @@ -0,0 +1,86 @@ +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Verify abort behaviour of transactor. +// +// The program will attempt to add an entry to a table called "pqxxevents", +// with a key column called "year"--and then abort the change. +// +// Note for the superstitious: the numbering for this test program is pure +// coincidence. +namespace +{ +// Let's take a boring year that is not going to be in the "pqxxevents" table +constexpr unsigned int BoringYear = 1977; + + +// Count events and specifically events occurring in Boring Year, leaving the +// former count in the result pair's first member, and the latter in second. +std::pair count_events(connection &conn, std::string const &table) +{ + work tx{conn}; + std::string const count_query{"SELECT count(*) FROM " + table}; + return std::make_pair( + tx.query_value(count_query), + tx.query_value(count_query + " WHERE year=" + to_string(BoringYear))); +} + + +struct deliberate_error : std::exception +{}; + + +void failed_insert(connection &C, std::string const &table) +{ + work tx(C); + result R = tx.exec0( + "INSERT INTO " + table + " VALUES (" + to_string(BoringYear) + + ", " + "'yawn')"); + + PQXX_CHECK_EQUAL(R.affected_rows(), 1, "Bad affected_rows()."); + throw deliberate_error(); +} + + +void test_013() +{ + connection conn; + { + work tx{conn}; + test::create_pqxxevents(tx); + tx.commit(); + } + + std::string const Table{"pqxxevents"}; + + auto const Before{ + perform([&conn, &Table] { return count_events(conn, Table); })}; + PQXX_CHECK_EQUAL( + Before.second, 0, + "Already have event for " + to_string(BoringYear) + "--can't test."); + + quiet_errorhandler d(conn); + PQXX_CHECK_THROWS( + perform([&conn, &Table] { failed_insert(conn, Table); }), deliberate_error, + "Failing transactor failed to throw correct exception."); + + auto const After{ + perform([&conn, &Table] { return count_events(conn, Table); })}; + + PQXX_CHECK_EQUAL( + After.first, Before.first, "abort() didn't reset event count."); + + PQXX_CHECK_EQUAL( + After.second, Before.second, + "abort() didn't reset event count for " + to_string(BoringYear)); +} + + +PQXX_REGISTER_TEST(test_013); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test14.cxx b/ext/libpqxx-7.7.3/test/test14.cxx new file mode 100644 index 000000000..c82f2323c --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test14.cxx @@ -0,0 +1,36 @@ +#include + +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test nontransaction. + +namespace +{ +void test_014() +{ + connection conn; + + // Begin a "non-transaction" acting on our current connection. This is + // really all the transactional integrity we need since we're only + // performing one query which does not modify the database. + nontransaction tx{conn, "test14"}; + + // The transaction class family also has process_notice() functions. + // These simply pass the notice through to their connection, but this may + // be more convenient in some cases. All process_notice() functions accept + // C++ strings as well as C strings. + tx.process_notice(std::string{"Started nontransaction\n"}); + + // "Commit" the non-transaction. This doesn't really do anything since + // nontransaction doesn't start a backend transaction. + tx.commit(); +} + + +PQXX_REGISTER_TEST(test_014); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test16.cxx b/ext/libpqxx-7.7.3/test/test16.cxx new file mode 100644 index 000000000..211bbba6a --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test16.cxx @@ -0,0 +1,46 @@ +#include + +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test robusttransaction. +namespace +{ +void test_016() +{ + connection conn; + robusttransaction<> tx{conn}; + result R{tx.exec("SELECT * FROM pg_tables")}; + + result::const_iterator c; + for (c = std::begin(R); c != std::end(R); ++c) + ; + + // See if back() and row comparison work properly + PQXX_CHECK( + std::size(R) >= 2, "Not enough rows in pg_tables to test, sorry!"); + + --c; + + PQXX_CHECK_EQUAL( + c->size(), std::size(R.back()), + "Size mismatch between row iterator and back()."); + + std::string const nullstr; + for (pqxx::row::size_type i{0}; i < c->size(); ++i) + PQXX_CHECK_EQUAL( + c[i].as(nullstr), R.back()[i].as(nullstr), "Value mismatch in back()."); + PQXX_CHECK(*c == R.back(), "Row equality is broken."); + PQXX_CHECK(not(*c != R.back()), "Row inequality is broken."); + + tx.commit(); +} + + +PQXX_REGISTER_TEST(test_016); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test17.cxx b/ext/libpqxx-7.7.3/test/test17.cxx new file mode 100644 index 000000000..7c99a35cc --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test17.cxx @@ -0,0 +1,29 @@ +#include + +#include +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Simple test program for libpqxx. Open connection to database, start +// a dummy transaction to gain nontransactional access, and perform a query. +namespace +{ +void test_017() +{ + connection conn; + perform([&conn] { + nontransaction tx{conn}; + auto const r{tx.exec("SELECT * FROM generate_series(1, 4)")}; + PQXX_CHECK_EQUAL(std::size(r), 4, "Weird query result."); + tx.commit(); + }); +} + + +PQXX_REGISTER_TEST(test_017); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test18.cxx b/ext/libpqxx-7.7.3/test/test18.cxx new file mode 100644 index 000000000..9718d2daa --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test18.cxx @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Verify abort behaviour of RobustTransaction. +// +// The program will attempt to add an entry to a table called "pqxxevents", +// with a key column called "year"--and then abort the change. +namespace +{ +// Let's take a boring year that is not going to be in the "pqxxevents" table +constexpr long BoringYear{1977}; + + +// Count events and specifically events occurring in Boring Year, leaving the +// former count in the result pair's first member, and the latter in second. +std::pair count_events(connection &conn, std::string const &table) +{ + nontransaction tx{conn}; + std::string const count_query{"SELECT count(*) FROM " + table}; + return std::make_pair( + tx.query_value(count_query), + tx.query_value(count_query + " WHERE year=" + to_string(BoringYear))); +} + + +struct deliberate_error : std::exception +{}; + + +void test_018() +{ + connection conn; + { + work tx{conn}; + test::create_pqxxevents(tx); + tx.commit(); + } + + std::string const Table{"pqxxevents"}; + + auto const Before{ + perform([&conn, &Table] { return count_events(conn, Table); })}; + PQXX_CHECK_EQUAL( + Before.second, 0, + "Already have event for " + to_string(BoringYear) + ", cannot run."); + + { + quiet_errorhandler d{conn}; + PQXX_CHECK_THROWS( + perform([&conn, Table] { + robusttransaction tx{conn}; + tx.exec0( + "INSERT INTO " + Table + " VALUES (" + to_string(BoringYear) + + ", '" + tx.esc("yawn") + "')"); + + throw deliberate_error(); + }), + deliberate_error, + "Not getting expected exception from failing transactor."); + } + + auto const After{ + perform([&conn, &Table] { return count_events(conn, Table); })}; + + PQXX_CHECK_EQUAL(After.first, Before.first, "Event count changed."); + PQXX_CHECK_EQUAL( + After.second, Before.second, + "Event count for " + to_string(BoringYear) + " changed."); +} + + +PQXX_REGISTER_TEST(test_018); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test20.cxx b/ext/libpqxx-7.7.3/test/test20.cxx new file mode 100644 index 000000000..1ddd4b09a --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test20.cxx @@ -0,0 +1,95 @@ +#include + +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test: nontransaction changes are not rolled back on abort. +namespace +{ +constexpr unsigned long BoringYear{1977}; + + +void test_020() +{ + connection conn; + nontransaction t1{conn}; + test::create_pqxxevents(t1); + + std::string const Table{"pqxxevents"}; + + // Verify our start condition before beginning: there must not be a 1977 + // record already. + result R(t1.exec(("SELECT * FROM " + Table + + " " + "WHERE year=" + + to_string(BoringYear)) + .c_str())); + PQXX_CHECK_EQUAL( + std::size(R), 0, + "Already have a row for " + to_string(BoringYear) + ", cannot test."); + + // (Not needed, but verify that clear() works on empty containers) + R.clear(); + PQXX_CHECK(std::empty(R), "result::clear() is broken."); + + // OK. Having laid that worry to rest, add a record for 1977. + t1.exec0( + "INSERT INTO " + Table + + " VALUES" + "(" + + to_string(BoringYear) + + "," + "'Yawn'" + ")"); + + // Abort T1. Since T1 is a nontransaction, which provides only the + // transaction class interface without providing any form of transactional + // integrity, this is not going to undo our work. + t1.abort(); + + // Verify that our record was added, despite the Abort() + nontransaction t2{conn, "t2"}; + R = t2.exec(("SELECT * FROM " + Table + + " " + "WHERE year=" + + to_string(BoringYear)) + .c_str()); + + PQXX_CHECK_EQUAL( + std::size(R), 1, + "Found wrong number of rows for " + to_string(BoringYear) + "."); + + PQXX_CHECK(R.capacity() >= std::size(R), "Result's capacity is too small."); + + R.clear(); + PQXX_CHECK(std::empty(R), "result::clear() doesn't work."); + + // Now remove our record again + t2.exec0( + "DELETE FROM " + Table + + " " + "WHERE year=" + + to_string(BoringYear)); + + t2.commit(); + + // And again, verify results + nontransaction t3{conn, "t3"}; + + R = t3.exec(("SELECT * FROM " + Table + + " " + "WHERE year=" + + to_string(BoringYear)) + .c_str()); + + PQXX_CHECK_EQUAL(std::size(R), 0, "Record still found after removal."); +} + + +PQXX_REGISTER_TEST(test_020); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test21.cxx b/ext/libpqxx-7.7.3/test/test21.cxx new file mode 100644 index 000000000..1090050d9 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test21.cxx @@ -0,0 +1,70 @@ +#include + +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Simple test program for libpqxx. Open a connection to database, start a +// transaction, and perform a query inside it. +namespace +{ +void test_021() +{ + connection conn; + + std::string const HostName{ + ((conn.hostname() == nullptr) ? "" : conn.hostname())}; + conn.process_notice( + std::string{} + "database=" + conn.dbname() + + ", " + "username=" + + conn.username() + + ", " + "hostname=" + + HostName + + ", " + "port=" + + to_string(conn.port()) + + ", " + "backendpid=" + + to_string(conn.backendpid()) + "\n"); + + work tx{conn, "test_021"}; + + // By now our connection should really have been created + conn.process_notice("Printing details on actual connection\n"); + conn.process_notice( + std::string{} + "database=" + conn.dbname() + + ", " + "username=" + + conn.username() + + ", " + "hostname=" + + HostName + + ", " + "port=" + + to_string(conn.port()) + + ", " + "backendpid=" + + to_string(conn.backendpid()) + "\n"); + + std::string P; + from_string(conn.port(), P); + PQXX_CHECK_EQUAL( + P, to_string(conn.port()), "Port string conversion is broken."); + PQXX_CHECK_EQUAL(to_string(P), P, "Port string conversion is broken."); + + result R(tx.exec("SELECT * FROM pg_tables")); + + tx.process_notice(pqxx::internal::concat( + to_string(std::size(R)), " result row in transaction ", tx.name(), "\n")); + tx.commit(); +} + + +PQXX_REGISTER_TEST(test_021); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test26.cxx b/ext/libpqxx-7.7.3/test/test26.cxx new file mode 100644 index 000000000..69b04d839 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test26.cxx @@ -0,0 +1,86 @@ +#include +#include + +#include +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Example program for libpqxx. Modify the database, retaining transactional +// integrity using the transactor framework. +namespace +{ +// Convert year to 4-digit format. +int To4Digits(int Y) +{ + int Result{Y}; + + PQXX_CHECK(Y >= 0, "Negative year: " + to_string(Y)); + + if (Y < 70) + Result += 2000; + else if (Y < 100) + Result += 1900; + else + PQXX_CHECK(Y >= 1970, "Unexpected year: " + to_string(Y)); + return Result; +} + + +// Transaction definition for year-field update. Returns conversions done. +std::map update_years(connection &C) +{ + std::map conversions; + work tx{C}; + + // Note all different years currently occurring in the table, writing them + // and their correct mappings to m_conversions + for (auto const &[y] : + tx.stream>("SELECT year FROM pqxxevents")) + { + // Read year, and if it is non-null, note its converted value + if (bool(y)) + conversions[y.value()] = To4Digits(y.value()); + } + + // For each occurring year, write converted date back to whereever it may + // occur in the table. Since we're in a transaction, any changes made by + // others at the same time will not affect us. + for (auto const &c : conversions) + tx.exec0( + "UPDATE pqxxevents " + "SET year=" + + to_string(c.second) + + " " + "WHERE year=" + + to_string(c.first)); + + tx.commit(); + + return conversions; +} + + +void test_026() +{ + connection conn; + { + nontransaction tx{conn}; + test::create_pqxxevents(tx); + tx.commit(); + } + + // Perform (an instantiation of) the UpdateYears transactor we've defined + // in the code above. This is where the work gets done. + auto const conversions{perform([&conn] { return update_years(conn); })}; + + PQXX_CHECK(not std::empty(conversions), "No conversions done!"); +} + + +PQXX_REGISTER_TEST(test_026); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test29.cxx b/ext/libpqxx-7.7.3/test/test29.cxx new file mode 100644 index 000000000..4547523f1 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test29.cxx @@ -0,0 +1,108 @@ +#include +#include +#include + +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Open connection to database, start a transaction, +// abort it, and verify that it "never happened." +// +// The program will attempt to add an entry to a table called "pqxxevents", +// with a key column called "year"--and then abort the change. +namespace +{ +// Let's take a boring year that is not going to be in the "pqxxevents" table +constexpr int BoringYear{1977}; + +std::string const Table{"pqxxevents"}; + + +// Count events, and boring events, in table +std::pair CountEvents(transaction_base &tx) +{ + std::string const events_query{"SELECT count(*) FROM " + Table}; + std::string const boring_query{ + events_query + " WHERE year=" + to_string(BoringYear)}; + + return std::make_pair( + tx.query_value(events_query), tx.query_value(boring_query)); +} + + +// Try adding a record, then aborting it, and check whether the abort was +// performed correctly. +void Test(connection &conn, bool ExplicitAbort) +{ + std::vector BoringRow{to_string(BoringYear), "yawn"}; + + std::pair EventCounts; + + // First run our doomed transaction. This will refuse to run if an event + // exists for our Boring Year. + { + // Begin a transaction acting on our current connection; we'll abort it + // later though. + work Doomed(conn, "Doomed"); + + // Verify that our Boring Year was not yet in the events table + EventCounts = CountEvents(Doomed); + + PQXX_CHECK_EQUAL( + EventCounts.second, 0, + "Can't run; " + to_string(BoringYear) + " is already in the table."); + + // Now let's try to introduce a row for our Boring Year + Doomed.exec0( + "INSERT INTO " + Table + + "(year, event) " + "VALUES (" + + to_string(BoringYear) + ", 'yawn')"); + + auto Recount{CountEvents(Doomed)}; + PQXX_CHECK_EQUAL(Recount.second, 1, "Unexpected number of events."); + PQXX_CHECK_EQUAL( + Recount.first, EventCounts.first + 1, "Number of events changed."); + + // Okay, we've added an entry but we don't really want to. Abort it + // explicitly if requested, or simply let the Transaction object "expire." + if (ExplicitAbort) + Doomed.abort(); + + // If now explicit abort requested, Doomed Transaction still ends here + } + + // Now check that we're back in the original state. Note that this may go + // wrong if somebody managed to change the table between our two + // transactions. + work Checkup(conn, "Checkup"); + + auto NewEvents{CountEvents(Checkup)}; + PQXX_CHECK_EQUAL( + NewEvents.first, EventCounts.first, "Wrong number of events."); + + PQXX_CHECK_EQUAL(NewEvents.second, 0, "Found unexpected events."); +} + + +void test_029() +{ + connection conn; + { + nontransaction tx{conn}; + test::create_pqxxevents(tx); + } + + // Test abort semantics, both with explicit and implicit abort + Test(conn, true); + Test(conn, false); +} + + +PQXX_REGISTER_TEST(test_029); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test30.cxx b/ext/libpqxx-7.7.3/test/test30.cxx new file mode 100644 index 000000000..fb6a05da0 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test30.cxx @@ -0,0 +1,73 @@ +#include +#include +#include + +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Query a table and report its metadata. +namespace +{ +void test_030() +{ + std::string const Table{"pg_tables"}; + + connection conn; + work tx{conn, "test30"}; + + result R(tx.exec(("SELECT * FROM " + Table).c_str())); + PQXX_CHECK(not std::empty(R), "Table " + Table + " is empty, cannot test."); + + // Print column names + for (pqxx::row::size_type c{0}; c < R.columns(); ++c) + { + std::string N{R.column_name(c)}; + + PQXX_CHECK_EQUAL( + R[0].column_number(N), R.column_number(N), + "row::column_number() is inconsistent with result::column_number()."); + + PQXX_CHECK_EQUAL(R[0].column_number(N), c, "Inconsistent column numbers."); + } + + // If there are rows in R, compare their metadata to R's. + if (std::empty(R)) + { + std::cout << "(Table is empty.)\n"; + return; + } + + PQXX_CHECK_EQUAL(R[0].rownumber(), 0, "Row 0 reports wrong number."); + + if (std::size(R) < 2) + std::cout << "(Only one row in table.)\n"; + else + PQXX_CHECK_EQUAL(R[1].rownumber(), 1, "Row 1 reports wrong number."); + + for (pqxx::row::size_type c{0}; c < std::size(R[0]); ++c) + { + std::string N{R.column_name(c)}; + + PQXX_CHECK_EQUAL( + std::string{R[0].at(c).c_str()}, R[0].at(N).c_str(), + "Different field values by name and by number."); + + PQXX_CHECK_EQUAL( + std::string{R[0][c].c_str()}, R[0][N].c_str(), + "at() is inconsistent with operator[]."); + + PQXX_CHECK_EQUAL(R[0][c].name(), N, "Inconsistent field names."); + + PQXX_CHECK_EQUAL( + std::size(R[0][c]), std::strlen(R[0][c].c_str()), + "Inconsistent field lengths."); + } +} + + +PQXX_REGISTER_TEST(test_030); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test32.cxx b/ext/libpqxx-7.7.3/test/test32.cxx new file mode 100644 index 000000000..90f0db68c --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test32.cxx @@ -0,0 +1,78 @@ +#include +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Verify abort behaviour of transactor. +// +// The program will attempt to add an entry to a table called "pqxxevents", +// with a key column called "year"--and then abort the change. +// +// Note for the superstitious: the numbering for this test program is pure +// coincidence. + +namespace +{ +// Let's take a boring year that is not going to be in the "pqxxevents" table +constexpr int BoringYear{1977}; + +std::pair count_events(connection &conn, std::string const &table) +{ + std::string const count_query{"SELECT count(*) FROM " + table}; + work tx{conn}; + return std::make_pair( + tx.query_value(count_query), + tx.query_value(count_query + " WHERE year=" + to_string(BoringYear))); +} + + +struct deliberate_error : std::exception +{}; + + +void test_032() +{ + connection conn; + { + nontransaction tx{conn}; + test::create_pqxxevents(tx); + } + + std::string const Table{"pqxxevents"}; + + std::pair const Before{ + perform([&conn, &Table] { return count_events(conn, Table); })}; + PQXX_CHECK_EQUAL( + Before.second, 0, + "Already have event for " + to_string(BoringYear) + ", cannot test."); + + { + quiet_errorhandler d(conn); + PQXX_CHECK_THROWS( + perform([&conn, &Table] { + work{conn}.exec0( + "INSERT INTO " + Table + " VALUES (" + to_string(BoringYear) + + ", " + "'yawn')"); + throw deliberate_error(); + }), + deliberate_error, + "Did not get expected exception from failing transactor."); + } + + std::pair const After{ + perform([&conn, &Table] { return count_events(conn, Table); })}; + + PQXX_CHECK_EQUAL(After.first, Before.first, "Event count changed."); + PQXX_CHECK_EQUAL( + After.second, Before.second, + "Event count for " + to_string(BoringYear) + " changed."); +} + + +PQXX_REGISTER_TEST(test_032); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test37.cxx b/ext/libpqxx-7.7.3/test/test37.cxx new file mode 100644 index 000000000..fe231d39b --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test37.cxx @@ -0,0 +1,78 @@ +#include +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Verify abort behaviour of RobustTransaction. +// +// The program will attempt to add an entry to a table called "pqxxevents", +// with a key column called "year"--and then abort the change. +namespace +{ +// Let's take a boring year that is not going to be in the "pqxxevents" table +constexpr int BoringYear{1977}; + +// Count events and specifically events occurring in Boring Year, leaving the +// former count in the result pair's first member, and the latter in second. +std::pair count_events(connection &conn, std::string const &table) +{ + std::string const count_query{"SELECT count(*) FROM " + table}; + nontransaction tx{conn}; + return std::make_pair( + tx.query_value(count_query), + tx.query_value(count_query + " WHERE year=" + to_string(BoringYear))); +} + + +struct deliberate_error : std::exception +{}; + + +void test_037() +{ + connection conn; + { + nontransaction tx{conn}; + test::create_pqxxevents(tx); + } + + std::string const Table{"pqxxevents"}; + + auto const Before{ + perform([&conn, &Table] { return count_events(conn, Table); })}; + PQXX_CHECK_EQUAL( + Before.second, 0, + "Already have event for " + to_string(BoringYear) + ", cannot test."); + + { + quiet_errorhandler d(conn); + PQXX_CHECK_THROWS( + perform([&conn, &Table] { + robusttransaction<> tx{conn}; + tx.exec0( + "INSERT INTO " + Table + " VALUES (" + to_string(BoringYear) + + ", " + "'yawn')"); + + throw deliberate_error(); + }), + deliberate_error, + "Did not get expected exception from failing transactor."); + } + + auto const After{ + perform([&conn, &Table] { return count_events(conn, Table); })}; + + PQXX_CHECK_EQUAL(After.first, Before.first, "Number of events changed."); + PQXX_CHECK_EQUAL( + After.second, Before.second, + "Number of events for " + to_string(BoringYear) + " changed."); +} + + +PQXX_REGISTER_TEST(test_037); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test39.cxx b/ext/libpqxx-7.7.3/test/test39.cxx new file mode 100644 index 000000000..d26855811 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test39.cxx @@ -0,0 +1,89 @@ +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test: nontransaction changes are committed immediately. +namespace +{ +int BoringYear{1977}; + + +void test_039() +{ + connection conn; + nontransaction tx1{conn}; + test::create_pqxxevents(tx1); + std::string const Table{"pqxxevents"}; + + // Verify our start condition before beginning: there must not be a 1977 + // record already. + result R(tx1.exec( + "SELECT * FROM " + Table + + " " + "WHERE year=" + + to_string(BoringYear))); + + PQXX_CHECK_EQUAL( + std::size(R), 0, + "Already have a row for " + to_string(BoringYear) + ", cannot test."); + + // (Not needed, but verify that clear() works on empty containers) + R.clear(); + PQXX_CHECK(std::empty(R), "Result is non-empty after clear()."); + + // OK. Having laid that worry to rest, add a record for 1977. + tx1.exec0( + "INSERT INTO " + Table + + " VALUES" + "(" + + to_string(BoringYear) + + "," + "'Yawn'" + ")"); + + // Abort tx1. Since tx1 is a nontransaction, which provides only the + // transaction class interface without providing any form of transactional + // integrity, this is not going to undo our work. + tx1.abort(); + + // Verify that our record was added, despite the Abort() + nontransaction tx2(conn, "tx2"); + R = tx2.exec( + "SELECT * FROM " + Table + + " " + "WHERE year=" + + to_string(BoringYear)); + PQXX_CHECK_EQUAL(std::size(R), 1, "Unexpected result size."); + + PQXX_CHECK(R.capacity() >= std::size(R), "Result's capacity is too small."); + + R.clear(); + PQXX_CHECK(std::empty(R), "result::clear() is broken."); + + // Now remove our record again + tx2.exec0( + "DELETE FROM " + Table + + " " + "WHERE year=" + + to_string(BoringYear)); + + tx2.commit(); + + // And again, verify results + nontransaction tx3(conn, "tx3"); + + R = tx3.exec( + "SELECT * FROM " + Table + + " " + "WHERE year=" + + to_string(BoringYear)); + + PQXX_CHECK_EQUAL(std::size(R), 0, "Record is not gone as expected."); +} + + +PQXX_REGISTER_TEST(test_039); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test46.cxx b/ext/libpqxx-7.7.3/test/test46.cxx new file mode 100644 index 000000000..0fa88915a --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test46.cxx @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Streams test program for libpqxx. Insert a result field into various +// types of streams. +namespace +{ +void test_046() +{ + connection conn; + work tx{conn}; + + row R{tx.exec1("SELECT count(*) FROM pg_tables")}; + + // Read the value into a stringstream. + std::stringstream I; + I << R[0]; + + // Now convert the stringstream into a numeric type + long L{}, L2{}; + I >> L; + + R[0].to(L2); + PQXX_CHECK_EQUAL(L, L2, "Inconsistency between conversion methods."); + + float F{}, F2{}; + std::stringstream I2; + I2 << R[0]; + I2 >> F; + R[0].to(F2); + PQXX_CHECK_BOUNDS(F2, F - 0.01, F + 0.01, "Bad floating-point result."); + + auto F3{from_string(R[0].c_str())}; + PQXX_CHECK_BOUNDS(F3, F - 0.01, F + 0.01, "Bad float from from_string."); + + auto D{from_string(R[0].c_str())}; + PQXX_CHECK_BOUNDS(D, F - 0.01, F + 0.01, "Bad double from from_string."); + + auto LD{from_string(R[0].c_str())}; + PQXX_CHECK_BOUNDS( + LD, F - 0.01, F + 0.01, "Bad long double from from_string."); + + auto S{from_string(R[0].c_str())}, + S2{from_string(std::string{R[0].c_str()})}, + S3{from_string(R[0])}; + + PQXX_CHECK_EQUAL( + S2, S, + "from_string(char const[], std::string &) " + "is inconsistent with " + "from_string(std::string const &, std::string &)."); + + PQXX_CHECK_EQUAL( + S3, S2, + "from_string(result::field const &, std::string &) " + "is inconsistent with " + "from_string(std::string const &, std::string &)."); + + PQXX_CHECK(tx.query_value("SELECT 1=1"), "1=1 doesn't yield 'true.'"); + + PQXX_CHECK(not tx.query_value("SELECT 2+2=5"), "2+2=5 yields 'true.'"); +} + + +PQXX_REGISTER_TEST(test_046); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test56.cxx b/ext/libpqxx-7.7.3/test/test56.cxx new file mode 100644 index 000000000..3aef757bb --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test56.cxx @@ -0,0 +1,24 @@ +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Simple test program for libpqxx. Issue invalid query and handle error. +namespace +{ +void test_056() +{ + connection conn; + work tx{conn}; + quiet_errorhandler d(conn); + + PQXX_CHECK_THROWS( + tx.exec("DELIBERATELY INVALID TEST QUERY..."), sql_error, + "SQL syntax error did not raise expected exception."); +} + + +PQXX_REGISTER_TEST(test_056); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test60.cxx b/ext/libpqxx-7.7.3/test/test60.cxx new file mode 100644 index 000000000..a29ce9ff7 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test60.cxx @@ -0,0 +1,85 @@ +#include + +#include +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Example program for libpqxx. Test session variable functionality. +namespace +{ +std::string GetDatestyle(connection &conn) +{ + return conn.get_var("DATESTYLE"); +} + + +std::string SetDatestyle(connection &conn, std::string style) +{ + conn.set_session_var("DATESTYLE", style); + std::string const fullname{GetDatestyle(conn)}; + PQXX_CHECK( + not std::empty(fullname), + "Setting datestyle to " + style + " makes it an empty string."); + + return fullname; +} + + +void CheckDatestyle(connection &conn, std::string expected) +{ + PQXX_CHECK_EQUAL(GetDatestyle(conn), expected, "Got wrong datestyle."); +} + + +void RedoDatestyle( + connection &conn, std::string const &style, std::string const &expected) +{ + PQXX_CHECK_EQUAL( + SetDatestyle(conn, style), expected, "Set wrong datestyle."); +} + + +void ActivationTest( + connection &conn, std::string const &style, std::string const &expected) +{ + RedoDatestyle(conn, style, expected); + CheckDatestyle(conn, expected); +} + + +void test_060() +{ + connection conn; + + PQXX_CHECK(not std::empty(GetDatestyle(conn)), "Initial datestyle not set."); + + std::string const ISOname{SetDatestyle(conn, "ISO")}; + std::string const SQLname{SetDatestyle(conn, "SQL")}; + + PQXX_CHECK_NOT_EQUAL(ISOname, SQLname, "Same datestyle in SQL and ISO."); + + RedoDatestyle(conn, "SQL", SQLname); + + ActivationTest(conn, "ISO", ISOname); + ActivationTest(conn, "SQL", SQLname); + + PQXX_CHECK_THROWS( + conn.set_session_var("bonjour_name", std::optional{}), + pqxx::variable_set_to_null, + "Setting a variable to null did not report the error correctly."); + + // Prove that setting an unknown variable causes an error, as expected + quiet_errorhandler d{conn}; + PQXX_CHECK_THROWS( + conn.set_session_var("NONEXISTENT_VARIABLE_I_HOPE", 1), sql_error, + "Setting unknown variable failed to fail."); +} + + +PQXX_REGISTER_TEST(test_060); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test61.cxx b/ext/libpqxx-7.7.3/test/test61.cxx new file mode 100644 index 000000000..a9c871bdb --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test61.cxx @@ -0,0 +1,61 @@ +#include + +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Example program for libpqxx. Test local variable functionality. +namespace +{ +std::string GetDatestyle(transaction_base &T) +{ + return T.conn().get_var("DATESTYLE"); +} + + +std::string SetDatestyle(transaction_base &T, std::string style) +{ + T.conn().set_session_var("DATESTYLE", style); + std::string const fullname{GetDatestyle(T)}; + PQXX_CHECK( + not std::empty(fullname), + "Setting datestyle to " + style + " makes it an empty string."); + + return fullname; +} + + +void RedoDatestyle( + transaction_base &T, std::string const &style, std::string const &expected) +{ + PQXX_CHECK_EQUAL(SetDatestyle(T, style), expected, "Set wrong datestyle."); +} + + +void test_061() +{ + connection conn; + work tx{conn}; + + PQXX_CHECK(not std::empty(GetDatestyle(tx)), "Initial datestyle not set."); + + std::string const ISOname{SetDatestyle(tx, "ISO")}; + std::string const SQLname{SetDatestyle(tx, "SQL")}; + + PQXX_CHECK_NOT_EQUAL(ISOname, SQLname, "Same datestyle in SQL and ISO."); + + RedoDatestyle(tx, "SQL", SQLname); + + // Prove that setting an unknown variable causes an error, as expected + quiet_errorhandler d(tx.conn()); + PQXX_CHECK_THROWS( + conn.set_session_var("NONEXISTENT_VARIABLE_I_HOPE", 1), sql_error, + "Setting unknown variable failed to fail."); +} + + +PQXX_REGISTER_TEST(test_061); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test62.cxx b/ext/libpqxx-7.7.3/test/test62.cxx new file mode 100644 index 000000000..d23e49e5a --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test62.cxx @@ -0,0 +1,61 @@ +#include +#include + +#include + +#include "test_helpers.hxx" + + +using namespace pqxx; + + +// Example program for libpqxx. Test binary string functionality. +namespace +{ +void test_062() +{ + connection conn; + work tx{conn}; + + std::string const TestStr{ + "Nasty\n\030Test\n\t String with \200\277 weird bytes " + "\r\0 and Trailer\\\\\0"}; + + tx.exec0("CREATE TEMP TABLE pqxxbin (binfield bytea)"); + + std::string const Esc{tx.esc_raw(std::basic_string{ + reinterpret_cast(std::data(TestStr)), + std::size(TestStr)})}; + + tx.exec0("INSERT INTO pqxxbin VALUES ('" + Esc + "')"); + + result R{tx.exec("SELECT * from pqxxbin")}; + tx.exec0("DELETE FROM pqxxbin"); + + auto const B{R.at(0).at(0).as>()}; + + PQXX_CHECK(not std::empty(B), "Binary string became empty in conversion."); + + PQXX_CHECK_EQUAL( + std::size(B), std::size(TestStr), "Binary string was mangled."); + + std::basic_string::const_iterator c; + std::basic_string::size_type i; + for (i = 0, c = std::begin(B); i < std::size(B); ++i, ++c) + { + PQXX_CHECK(c != std::end(B), "Premature end to binary string."); + + char const x{TestStr.at(i)}, y{char(B.at(i))}, z{char(std::data(B)[i])}; + + PQXX_CHECK_EQUAL( + std::string(&x, 1), std::string(&y, 1), "Binary string byte changed."); + + PQXX_CHECK_EQUAL( + std::string(&y, 1), std::string(&z, 1), + "Inconsistent byte at offset " + to_string(i) + "."); + } +} + + +PQXX_REGISTER_TEST(test_062); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test69.cxx b/ext/libpqxx-7.7.3/test/test69.cxx new file mode 100644 index 000000000..d15ff1489 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test69.cxx @@ -0,0 +1,55 @@ +#include + +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Issue a query repeatedly through a pipeline, and +// compare results. +namespace +{ +void TestPipeline(pipeline &P, int numqueries) +{ + std::string const Q{"SELECT 99"}; + + for (int i{numqueries}; i > 0; --i) P.insert(Q); + + PQXX_CHECK( + (numqueries == 0) or not std::empty(P), "pipeline::empty() is broken."); + + int res{0}; + for (int i{numqueries}; i > 0; --i) + { + PQXX_CHECK( + not std::empty(P), "Got wrong number of queries from pipeline."); + + auto R{P.retrieve()}; + + if (res != 0) + PQXX_CHECK_EQUAL( + R.second[0][0].as(), res, + "Got unexpected result out of pipeline."); + + res = R.second[0][0].as(); + } + + PQXX_CHECK(std::empty(P), "Pipeline not empty after retrieval."); +} + + +void test_069() +{ + connection conn; + work tx{conn}; + pipeline P(tx); + PQXX_CHECK(std::empty(P), "Pipeline is not empty initially."); + for (int i{0}; i < 5; ++i) TestPipeline(P, i); +} + + +PQXX_REGISTER_TEST(test_069); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/test70.cxx b/ext/libpqxx-7.7.3/test/test70.cxx new file mode 100644 index 000000000..0f72089f1 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test70.cxx @@ -0,0 +1,101 @@ +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +namespace +{ +void TestPipeline(pipeline &P, int numqueries) +{ + std::string const Q{"SELECT * FROM generate_series(1, 10)"}; + result const Empty; + PQXX_CHECK(std::empty(Empty), "Default-constructed result is not empty."); + PQXX_CHECK( + std::empty(Empty.query()), "Default-constructed result has query"); + + P.retain(); + for (int i{numqueries}; i > 0; --i) P.insert(Q); + P.resume(); + + PQXX_CHECK( + (numqueries == 0) || not std::empty(P), "pipeline::empty() is broken."); + + int res{0}; + result Prev; + PQXX_CHECK_EQUAL(Prev, Empty, "Default-constructed results are not equal."); + + for (int i{numqueries}; i > 0; --i) + { + PQXX_CHECK(not std::empty(P), "Got no results from pipeline."); + + auto R{P.retrieve()}; + + PQXX_CHECK_NOT_EQUAL(R.second, Empty, "Got empty result."); + if (Prev != Empty) + PQXX_CHECK_EQUAL(R.second, Prev, "Results to same query are different."); + + Prev = R.second; + PQXX_CHECK_EQUAL(Prev, R.second, "Assignment breaks result equality."); + PQXX_CHECK_EQUAL(R.second.query(), Q, "Result is for unexpected query."); + + if (res != 0) + PQXX_CHECK_EQUAL(Prev[0][0].as(), res, "Bad result from pipeline."); + + res = Prev[0][0].as(); + } + + PQXX_CHECK(std::empty(P), "Pipeline was not empty after retrieval."); +} + + +// Test program for libpqxx. Issue a query repeatedly through a pipeline, and +// compare results. Use retain() and resume() for performance. +void test_070() +{ + connection conn; + work tx{conn}; + pipeline P(tx); + + PQXX_CHECK(std::empty(P), "Pipeline is not empty initially."); + + // Try to confuse the pipeline by feeding it a query and flushing + P.retain(); + std::string const Q{"SELECT * FROM pg_tables"}; + P.insert(Q); + P.flush(); + + PQXX_CHECK(std::empty(P), "Pipeline was not empty after flush()."); + + // See if complete() breaks retain() as it should + P.retain(); + P.insert(Q); + PQXX_CHECK(not std::empty(P), "Pipeline was empty after insert()."); + P.complete(); + PQXX_CHECK(not std::empty(P), "complete() emptied pipeline."); + + PQXX_CHECK_EQUAL( + P.retrieve().second.query(), Q, "Result is for wrong query."); + + PQXX_CHECK(std::empty(P), "Pipeline not empty after retrieve()."); + + // See if retrieve() breaks retain() when it needs to + P.retain(); + P.insert(Q); + PQXX_CHECK_EQUAL( + P.retrieve().second.query(), Q, "Got result for wrong query."); + + // See if regular retain()/resume() works + for (int i{0}; i < 5; ++i) TestPipeline(P, i); + + // See if retrieve() fails on an empty pipeline, as it should + quiet_errorhandler d(conn); + PQXX_CHECK_THROWS_EXCEPTION( + P.retrieve(), "Empty pipeline allows retrieve()."); +} +} // namespace + + +PQXX_REGISTER_TEST(test_070); diff --git a/ext/libpqxx-7.7.3/test/test71.cxx b/ext/libpqxx-7.7.3/test/test71.cxx new file mode 100644 index 000000000..e669d9ad5 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test71.cxx @@ -0,0 +1,74 @@ +#include +#include + +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Issue queries through a pipeline, and retrieve +// results both in-order and out-of-order. +namespace +{ +using Exp = std::map; + +template void checkresult(pipeline &P, PAIR c) +{ + result const r{P.retrieve(c.first)}; + int const val{r.at(0).at(0).as(int(0))}; + PQXX_CHECK_EQUAL(val, c.second, "Wrong result from pipeline."); +} + + +void test_071() +{ + connection conn; + work tx{conn}; + pipeline P(tx); + + // Keep expected result for every query we issue + Exp values; + + // Insert queries returning various numbers. + for (int i{1}; i < 10; ++i) values[P.insert("SELECT " + to_string(i))] = i; + + // Retrieve results in query_id order, and compare to expected values + for (auto &c : values) checkresult(P, c); + + PQXX_CHECK(std::empty(P), "Pipeline was not empty retrieving all results."); + + values.clear(); + + // Insert more queries returning various numbers + P.retain(20); + for (int i{100}; i > 90; --i) values[P.insert("SELECT " + to_string(i))] = i; + + P.resume(); + + // Retrieve results in reverse order + for (auto c{std::rbegin(values)}; c != std::rend(values); ++c) + checkresult(P, *c); + + values.clear(); + P.retain(10); + for (int i{1010}; i > 1000; --i) + values[P.insert("SELECT " + to_string(i))] = i; + for (auto &c : values) + { + if (P.is_finished(c.first)) + std::cout << "Query #" << c.first << " completed despite retain()" + << std::endl; + } + + // See that all results are retrieved by complete() + P.complete(); + for (auto &c : values) + PQXX_CHECK(P.is_finished(c.first), "Query not finished after complete()."); +} +} // namespace + + +PQXX_REGISTER_TEST(test_071); diff --git a/ext/libpqxx-7.7.3/test/test72.cxx b/ext/libpqxx-7.7.3/test/test72.cxx new file mode 100644 index 000000000..716081ed7 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test72.cxx @@ -0,0 +1,53 @@ +#include + +#include +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Test error handling for pipeline. +namespace +{ +void test_072() +{ + connection conn; + work tx{conn}; + pipeline P{tx}; + + // Ensure all queries are issued at once to make the test more interesting + P.retain(); + + // The middle query should fail; the surrounding two should succeed + auto const id_1{P.insert("SELECT 1")}; + auto const id_f{P.insert("SELECT * FROM pg_nonexist")}; + auto const id_2{P.insert("SELECT 2")}; + + // See that we can process the queries without stumbling over the error + P.complete(); + + // We should be able to get the first result, which preceeds the error + auto const res_1{P.retrieve(id_1).at(0).at(0).as()}; + PQXX_CHECK_EQUAL(res_1, 1, "Got wrong result from pipeline."); + + // We should *not* get a result for the query behind the error + { + quiet_errorhandler d{conn}; + PQXX_CHECK_THROWS( + P.retrieve(id_2).at(0).at(0).as(), std::runtime_error, + "Pipeline wrongly resumed after SQL error."); + } + + // Now see that we get an exception when we touch the failed result + { + quiet_errorhandler d{conn}; + PQXX_CHECK_THROWS( + P.retrieve(id_f), sql_error, "Pipeline failed to register SQL error."); + } +} +} // namespace + + +PQXX_REGISTER_TEST(test_072); diff --git a/ext/libpqxx-7.7.3/test/test74.cxx b/ext/libpqxx-7.7.3/test/test74.cxx new file mode 100644 index 000000000..ff1abd993 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test74.cxx @@ -0,0 +1,72 @@ +#include + +#include + +#include "test_helpers.hxx" + +using namespace pqxx; + + +// Test program for libpqxx. Test fieldstream. +namespace +{ +void test_074() +{ + connection conn; + work tx{conn}; + + result R{tx.exec("SELECT * FROM pg_tables")}; + std::string const sval{R.at(0).at(1).c_str()}; + std::string sval2; + fieldstream fs1(R.front()[1]); + fs1 >> sval2; + PQXX_CHECK_EQUAL(sval2, sval, "fieldstream returned wrong value."); + + R = tx.exec("SELECT count(*) FROM pg_tables"); + int ival; + fieldstream fs2(R.at(0).at(0)); + fs2 >> ival; + PQXX_CHECK_EQUAL( + ival, R.front().front().as(), "fieldstream::front() is broken."); + + double dval; + (fieldstream(R.at(0).at(0))) >> dval; + PQXX_CHECK_BOUNDS( + dval, R[0][0].as() - 0.1, R[0][0].as() + 0.1, + "Got wrong double from fieldstream."); + + auto const roughpi{static_cast(3.1415926435)}; + R = tx.exec("SELECT " + to_string(roughpi)); + float pival; + (fieldstream(R.at(0).at(0))) >> pival; + PQXX_CHECK_BOUNDS( + pival, roughpi - 0.001, roughpi + 0.001, + "Pi approximation came back wrong from fieldstream."); + + PQXX_CHECK_EQUAL( + to_string(R[0][0]), R[0][0].c_str(), + "to_string(result::field) is inconsistent with c_str()."); + + float float_pi; + from_string(to_string(roughpi), float_pi); + PQXX_CHECK_BOUNDS( + float_pi, roughpi - 0.00001, roughpi + 0.00001, + "Float changed in conversion."); + + double double_pi; + pqxx::from_string(pqxx::to_string(static_cast(roughpi)), double_pi); + PQXX_CHECK_BOUNDS( + double_pi, roughpi - 0.00001, roughpi + 0.00001, + "Double changed in conversion."); + + long double const ld{roughpi}; + long double long_double_pi; + from_string(to_string(ld), long_double_pi); + PQXX_CHECK_BOUNDS( + long_double_pi, roughpi - 0.00001, roughpi + 0.00001, + "long double changed in conversion."); +} +} // namespace + + +PQXX_REGISTER_TEST(test_074); diff --git a/ext/libpqxx-7.7.3/test/test75.cxx b/ext/libpqxx-7.7.3/test/test75.cxx new file mode 100644 index 000000000..07f00141e --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test75.cxx @@ -0,0 +1,108 @@ +#include +#include + +#include + +#include "test_helpers.hxx" + + +// Test program for libpqxx. Compare const_reverse_iterator iteration of a +// result to a regular, const_iterator iteration. +namespace +{ +void test_075() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + + pqxx::test::create_pqxxevents(tx); + auto const R(tx.exec("SELECT year FROM pqxxevents")); + PQXX_CHECK(not std::empty(R), "No events found, cannot test."); + + PQXX_CHECK_EQUAL(R[0], R.at(0), "Inconsistent result indexing."); + PQXX_CHECK(not(R[0] != R.at(0)), "result::row::operator!=() is broken."); + + PQXX_CHECK_EQUAL(R[0][0], R[0].at(0), "Inconsistent row indexing."); + PQXX_CHECK( + not(R[0][0] != R[0].at(0)), "result::field::operator!=() is broken."); + + std::vector contents; + for (auto const &i : R) contents.push_back(i.at(0).as()); + + PQXX_CHECK_EQUAL( + std::size(contents), std::vector::size_type(std::size(R)), + "Number of values does not match result size."); + + for (pqxx::result::size_type i{0}; i < std::size(R); ++i) + PQXX_CHECK_EQUAL( + contents[static_cast(i)], R.at(i).at(0).c_str(), + "Inconsistent iteration."); + + // Thorough test for result::const_reverse_iterator + pqxx::result::const_reverse_iterator ri1(std::rbegin(R)), ri2(ri1), + ri3(std::end(R)); + ri2 = std::rbegin(R); + + PQXX_CHECK(ri2 == ri1, "reverse_iterator copy constructor is broken."); + PQXX_CHECK(ri3 == ri2, "result::end() does not generate rbegin()."); + PQXX_CHECK_EQUAL( + ri2 - ri3, 0, + "const_reverse_iterator is at nonzero distance from its own copy."); + + PQXX_CHECK(ri2 == ri3 + 0, "reverse_iterator+0 gives strange result."); + PQXX_CHECK(ri2 == ri3 - 0, "reverse_iterator-0 gives strange result."); + PQXX_CHECK(not(ri3 < ri2), "operator<() breaks on equal reverse_iterators."); + PQXX_CHECK(ri2 <= ri3, "operator<=() breaks on equal reverse_iterators."); + + PQXX_CHECK(ri3++ == ri2, "reverse_iterator post-increment is broken."); + + PQXX_CHECK_EQUAL(ri3 - ri2, 1, "Wrong nonzero reverse_iterator distance."); + PQXX_CHECK(ri3 > ri2, "reverse_iterator operator>() is broken."); + PQXX_CHECK(ri3 >= ri2, "reverse_iterator operator>=() is broken."); + PQXX_CHECK(ri2 < ri3, "reverse_iterator operator<() is broken."); + PQXX_CHECK(ri2 <= ri3, "reverse_iterator operator<=() is broken."); + PQXX_CHECK(ri3 == ri2 + 1, "Adding int to reverse_iterator is broken."); + PQXX_CHECK( + ri2 == ri3 - 1, "Subtracting int from reverse_iterator is broken."); + + PQXX_CHECK(ri3 == ++ri2, "reverse_iterator pre-increment is broken."); + PQXX_CHECK(ri3 >= ri2, "operator>=() breaks on equal reverse_iterators."); + PQXX_CHECK(ri3 >= ri2, "operator<=() breaks on equal reverse_iterators."); + + PQXX_CHECK( + *ri3.base() == R.back(), "reverse_iterator does not arrive at back()."); + + PQXX_CHECK( + ri1->at(0) == (*ri1).at(0), + "reverse_iterator operator->() is inconsistent with operator*()."); + + PQXX_CHECK(ri2-- == ri3, "reverse_iterator post-decrement is broken."); + PQXX_CHECK(ri2 == --ri3, "reverse_iterator pre-decrement is broken."); + PQXX_CHECK(ri2 == std::rbegin(R), "reverse_iterator decrement is broken."); + + ri2 += 1; + ri3 -= -1; + + PQXX_CHECK( + ri2 != std::rbegin(R), "Adding to reverse_iterator does not work."); + PQXX_CHECK( + ri3 == ri2, "reverse_iterator operator-=() breaks on negative distances."); + + ri2 -= 1; + PQXX_CHECK( + ri2 == std::rbegin(R), + "reverse_iterator operator+=() and operator-=() do not cancel out."); + + // Now verify that reverse iterator also sees the same results... + auto l{std::rbegin(contents)}; + for (auto i{std::rbegin(R)}; i != std::rend(R); ++i, ++l) + PQXX_CHECK_EQUAL(*l, i->at(0).c_str(), "Inconsistent reverse iteration."); + + PQXX_CHECK(l == std::rend(contents), "Reverse iteration ended too soon."); + + PQXX_CHECK(not std::empty(R), "No events found in table, cannot test."); +} +} // namespace + + +PQXX_REGISTER_TEST(test_075); diff --git a/ext/libpqxx-7.7.3/test/test76.cxx b/ext/libpqxx-7.7.3/test/test76.cxx new file mode 100644 index 000000000..122c0445c --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test76.cxx @@ -0,0 +1,52 @@ +#include + +#include "test_helpers.hxx" + +// Simple test program for libpqxx. Test string conversion routines. +namespace +{ +void test_076() +{ + pqxx::connection conn; + pqxx::nontransaction tx{conn}; + + auto RFalse{tx.exec1("SELECT 1=0")}, RTrue{tx.exec1("SELECT 1=1")}; + auto False{pqxx::from_string(RFalse[0])}, + True{pqxx::from_string(RTrue[0])}; + PQXX_CHECK(not False, "False bool converted to true."); + PQXX_CHECK(True, "True bool converted to false."); + + RFalse = tx.exec1("SELECT " + pqxx::to_string(False)); + RTrue = tx.exec1("SELECT " + pqxx::to_string(True)); + False = pqxx::from_string(RFalse[0]); + True = pqxx::from_string(RTrue[0]); + PQXX_CHECK(not False, "False bool converted to true."); + PQXX_CHECK(True, "True bool converted to false."); + + short const svals[]{-1, 1, 999, -32767, -32768, 32767, 0}; + for (int i{0}; svals[i] != 0; ++i) + { + auto s{pqxx::from_string(pqxx::to_string(svals[i]))}; + PQXX_CHECK_EQUAL(s, svals[i], "short/string conversion not bijective."); + s = pqxx::from_string( + tx.exec1("SELECT " + pqxx::to_string(svals[i]))[0].c_str()); + PQXX_CHECK_EQUAL(s, svals[i], "Roundtrip through backend changed short."); + } + + unsigned short const uvals[]{1, 999, 32767, 32768, 65535, 0}; + for (int i{0}; uvals[i] != 0; ++i) + { + auto u{pqxx::from_string(pqxx::to_string(uvals[i]))}; + PQXX_CHECK_EQUAL( + u, uvals[i], "unsigned short/string conversion not bijective."); + + u = pqxx::from_string( + tx.exec1("SELECT " + pqxx::to_string(uvals[i]))[0].c_str()); + PQXX_CHECK_EQUAL( + u, uvals[i], "Roundtrip through backend changed unsigned short."); + } +} +} // namespace + + +PQXX_REGISTER_TEST(test_076); diff --git a/ext/libpqxx-7.7.3/test/test77.cxx b/ext/libpqxx-7.7.3/test/test77.cxx new file mode 100644 index 000000000..88e83c26d --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test77.cxx @@ -0,0 +1,28 @@ +#include + +#include "test_helpers.hxx" + + +// Test program for libpqxx. Test result::swap() +namespace +{ +void test_077() +{ + pqxx::connection conn; + pqxx::nontransaction tx{conn}; + + auto RFalse{tx.exec("SELECT 1=0")}, RTrue{tx.exec("SELECT 1=1")}; + auto f{pqxx::from_string(RFalse[0][0])}; + auto t{pqxx::from_string(RTrue[0][0])}; + PQXX_CHECK( + not f and t, "Booleans converted incorrectly; can't trust this test."); + + RFalse.swap(RTrue); + f = pqxx::from_string(RFalse[0][0]); + t = pqxx::from_string(RTrue[0][0]); + PQXX_CHECK(f and not t, "result::swap() is broken."); +} +} // namespace + + +PQXX_REGISTER_TEST(test_077); diff --git a/ext/libpqxx-7.7.3/test/test78.cxx b/ext/libpqxx-7.7.3/test/test78.cxx new file mode 100644 index 000000000..f970cc926 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test78.cxx @@ -0,0 +1,69 @@ +#include +#include +#include + +#include +#include +#include + +#include "test_helpers.hxx" + + +// Example program for libpqxx. Send notification to self, using a +// notification name with unusal characters, and without polling. +namespace +{ +// Sample implementation of notification receiver. +class TestListener : public pqxx::notification_receiver +{ + bool m_done; + +public: + explicit TestListener(pqxx::connection &conn, std::string const &Name) : + pqxx::notification_receiver(conn, Name), m_done(false) + {} + + void operator()(std::string const &, int be_pid) override + { + m_done = true; + PQXX_CHECK_EQUAL( + be_pid, conn().backendpid(), + "Got notification from wrong backend process."); + + std::cout << "Received notification: " << channel() << " pid=" << be_pid + << std::endl; + } + + bool done() const { return m_done; } +}; + + +void test_078() +{ + pqxx::connection conn; + + std::string const NotifName{"my listener"}; + TestListener L{conn, NotifName}; + + pqxx::perform([&conn, &L] { + pqxx::work tx{conn}; + tx.exec0("NOTIFY " + tx.quote_name(L.channel())); + tx.commit(); + }); + + int notifs{0}; + for (int i{0}; (i < 20) and not L.done(); ++i) + { + PQXX_CHECK_EQUAL(notifs, 0, "Got unexpected notifications."); + std::cout << "."; + notifs = conn.await_notification(); + } + std::cout << std::endl; + + PQXX_CHECK(L.done(), "No notification received."); + PQXX_CHECK_EQUAL(notifs, 1, "Got unexpected number of notifications."); +} +} // namespace + + +PQXX_REGISTER_TEST(test_078); diff --git a/ext/libpqxx-7.7.3/test/test79.cxx b/ext/libpqxx-7.7.3/test/test79.cxx new file mode 100644 index 000000000..761e5f3c3 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test79.cxx @@ -0,0 +1,70 @@ +#include +#include +#include + +#include +#include +#include + +#include "test_helpers.hxx" + + +// Example program for libpqxx. Test waiting for notification with timeout. +namespace +{ +// Sample implementation of notification receiver. +class TestListener final : public pqxx::notification_receiver +{ + bool m_done; + +public: + explicit TestListener(pqxx::connection &conn, std::string const &Name) : + pqxx::notification_receiver(conn, Name), m_done(false) + {} + + void operator()(std::string const &, int be_pid) override + { + m_done = true; + PQXX_CHECK_EQUAL( + be_pid, conn().backendpid(), "Notification came from wrong backend."); + + std::cout << "Received notification: " << channel() << " pid=" << be_pid + << std::endl; + } + + bool done() const { return m_done; } +}; + + +void test_079() +{ + pqxx::connection conn; + + std::string const NotifName{"mylistener"}; + TestListener L(conn, NotifName); + + // First see if the timeout really works: we're not expecting any notifs + int notifs{conn.await_notification(0, 1)}; + PQXX_CHECK_EQUAL(notifs, 0, "Got unexpected notification."); + + pqxx::perform([&conn, &L] { + pqxx::work tx{conn}; + tx.exec0("NOTIFY " + L.channel()); + tx.commit(); + }); + + for (int i{0}; (i < 20) and not L.done(); ++i) + { + PQXX_CHECK_EQUAL(notifs, 0, "Got notifications, but no handler called."); + std::cout << "."; + notifs = conn.await_notification(1, 0); + } + std::cout << std::endl; + + PQXX_CHECK(L.done(), "No notifications received."); + PQXX_CHECK_EQUAL(notifs, 1, "Got unexpected notifications."); +} +} // namespace + + +PQXX_REGISTER_TEST(test_079); diff --git a/ext/libpqxx-7.7.3/test/test82.cxx b/ext/libpqxx-7.7.3/test/test82.cxx new file mode 100644 index 000000000..e7b2769c7 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test82.cxx @@ -0,0 +1,154 @@ +#include + +#include + +#include "test_helpers.hxx" + + +// Test program for libpqxx. Read and print table using row iterators. +namespace +{ +void test_082() +{ + pqxx::connection conn; + pqxx::nontransaction tx{conn}; + + pqxx::test::create_pqxxevents(tx); + std::string const Table{"pqxxevents"}; + pqxx::result R{tx.exec("SELECT * FROM " + Table)}; + + PQXX_CHECK(not std::empty(R), "Got empty result."); + + std::string const nullstr("[null]"); + + for (auto const &r : R) + { + pqxx::row::const_iterator f2(r[0]); + for (auto const &f : r) + { + PQXX_CHECK_EQUAL( + (*f2).as(nullstr), f.as(nullstr), "Inconsistent iteration result."); + ++f2; + } + + PQXX_CHECK( + std::begin(r) + pqxx::row::difference_type(std::size(r)) == std::end(r), + "Row end() appears to be in the wrong place."); + PQXX_CHECK( + pqxx::row::difference_type(std::size(r)) + std::begin(r) == std::end(r), + "Row iterator addition is not commutative."); + PQXX_CHECK_EQUAL( + std::begin(r)->num(), 0, "Wrong column number at begin()."); + + pqxx::row::const_iterator f3(r[std::size(r)]); + + PQXX_CHECK(f3 == std::end(r), "Did not get end() at end of row."); + + PQXX_CHECK( + f3 > std::begin(r), "Row end() appears to precede its begin()."); + + PQXX_CHECK( + f3 >= std::end(r) and std::begin(r) < f3, + "Row iterator operator<() is broken."); + + PQXX_CHECK(f3 > std::begin(r), "Row end() not greater than begin()."); + + pqxx::row::const_iterator f4{r, std::size(r)}; + PQXX_CHECK(f4 == f3, "Row iterator constructor with offset is broken."); + + --f3; + f4 -= 1; + + PQXX_CHECK(f3 < std::end(r), "Last field in row is not before end()."); + PQXX_CHECK(f3 >= std::begin(r), "Last field in row precedes begin()."); + PQXX_CHECK( + f3 == std::end(r) - 1, "Back from end() doese not yield end()-1."); + PQXX_CHECK_EQUAL( + std::end(r) - f3, 1, "Wrong distance from last row to end()."); + + PQXX_CHECK(f4 == f3, "Row iterator operator-=() is broken."); + f4 += 1; + PQXX_CHECK(f4 == std::end(r), "Row iterator operator+=() is broken."); + + for (auto fr = std::rbegin(r); fr != std::rend(r); ++fr, --f3) + PQXX_CHECK_EQUAL( + *fr, *f3, + "Reverse traversal is not consistent with forward traversal."); + } + + // Thorough test for row::const_reverse_iterator + pqxx::row::const_reverse_iterator ri1(std::rbegin(R.front())), ri2(ri1), + ri3(std::end(R.front())); + ri2 = std::rbegin(R.front()); + + PQXX_CHECK( + ri1 == ri2, "Copy-constructed reverse_iterator is not equal to original."); + + PQXX_CHECK(ri2 == ri3, "result::end() does not generate rbegin()."); + PQXX_CHECK_EQUAL( + ri2 - ri3, 0, + "Distance between identical const_reverse_iterators was nonzero."); + + PQXX_CHECK( + pqxx::row::const_reverse_iterator(ri1.base()) == ri1, + "Back-conversion of reverse_iterator base() fails."); + + PQXX_CHECK(ri2 == ri3 + 0, "reverse_iterator+0 gives strange result."); + PQXX_CHECK(ri2 == ri3 - 0, "reverse_iterator-0 gives strange result."); + + PQXX_CHECK( + not(ri3 < ri2), + "reverse_iterator operator<() breaks on identical iterators."); + PQXX_CHECK( + ri2 <= ri3, + "reverse_iterator operator<=() breaks on identical iterators."); + PQXX_CHECK(ri3++ == ri2, "reverse_iterator post-increment is broken."); + + PQXX_CHECK_EQUAL(ri3 - ri2, 1, "Wrong reverse_iterator distance."); + PQXX_CHECK(ri3 > ri2, "reverse_iterator operator>() is broken."); + PQXX_CHECK(ri3 >= ri2, "reverse_iterator operator>=() is broken."); + PQXX_CHECK(ri2 < ri3, "reverse_iterator operator<() is broken."); + PQXX_CHECK(ri2 <= ri3, "reverse_iterator operator<=() is broken."); + PQXX_CHECK(ri3 == ri2 + 1, "Adding number to reverse_iterator goes wrong."); + PQXX_CHECK(ri2 == ri3 - 1, "Subtracting from reverse_iterator goes wrong."); + + PQXX_CHECK( + ri3 == ++ri2, "reverse_iterator pre-incremen returns wrong result."); + + PQXX_CHECK( + ri3 >= ri2, "reverse_iterator operator>=() breaks on equal iterators."); + PQXX_CHECK( + ri3 >= ri2, "reverse_iterator operator<=() breaks on equal iterators."); + PQXX_CHECK( + *ri3.base() == R.front().back(), + "reverse_iterator does not arrive at back()."); + PQXX_CHECK( + ri1->c_str()[0] == (*ri1).c_str()[0], + "reverse_iterator operator->() is inconsistent with operator*()."); + PQXX_CHECK( + ri2-- == ri3, "reverse_iterator post-decrement returns wrong result."); + PQXX_CHECK( + ri2 == --ri3, "reverse_iterator pre-increment returns wrong result."); + PQXX_CHECK( + ri2 == std::rbegin(R.front()), + "Moving iterator back and forth doesn't get it back to origin."); + + ri2 += 1; + ri3 -= -1; + + PQXX_CHECK( + ri2 != std::rbegin(R.front()), "Adding to reverse_iterator doesn't work."); + PQXX_CHECK( + ri2 != std::rbegin(R.front()), "Adding to reverse_iterator doesn't work."); + PQXX_CHECK( + ri3 == ri2, "reverse_iterator operator-=() breaks on negative numbers."); + + ri2 -= 1; + PQXX_CHECK( + ri2 == std::rbegin(R.front()), + "reverse_iterator operator+=() and operator-=() do not cancel out"); +} +} // namespace + + +PQXX_REGISTER_TEST(test_082); diff --git a/ext/libpqxx-7.7.3/test/test84.cxx b/ext/libpqxx-7.7.3/test/test84.cxx new file mode 100644 index 000000000..d35dc521b --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test84.cxx @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "test_helpers.hxx" + + +// "Adopted SQL Cursor" test program for libpqxx. Create SQL cursor, wrap it +// in a cursor stream, then use it to fetch data and check for consistent +// results. Compare results against an icursor_iterator so that is tested as +// well. +namespace +{ +void test_084() +{ + pqxx::connection conn; + pqxx::transaction tx{conn}; + + std::string const Table{"pg_tables"}, Key{"tablename"}; + + // Count rows. + pqxx::result R(tx.exec("SELECT count(*) FROM " + Table)); + + PQXX_CHECK( + R.at(0).at(0).as() > 20, + "Not enough rows in " + Table + ", cannot test."); + + // Create an SQL cursor and, for good measure, muddle up its state a bit. + std::string const CurName{"MYCUR"}, + Query{"SELECT * FROM " + Table + " ORDER BY " + Key}; + constexpr int InitialSkip{2}, GetRows{3}; + + tx.exec0("DECLARE " + tx.quote_name(CurName) + " CURSOR FOR " + Query); + tx.exec0( + "MOVE " + pqxx::to_string(InitialSkip * GetRows) + + " " + "IN " + + tx.quote_name(CurName)); + + // Wrap cursor in cursor stream. Apply some trickery to get its name inside + // a result field for this purpose. This isn't easy because it's not + // supposed to be easy; normally we'd only construct streams around existing + // SQL cursors if they were being returned by functions. + pqxx::icursorstream C{ + tx, tx.exec("SELECT '" + tx.esc(CurName) + "'")[0][0], GetRows}; + + // Create parallel cursor to check results + pqxx::icursorstream C2{tx, Query, "CHECKCUR", GetRows}; + pqxx::icursor_iterator i2{C2}; + + // Remember, our adopted cursor is at position (InitialSkip*GetRows) + pqxx::icursor_iterator i3(i2); + + PQXX_CHECK( + (i3 == i2) and not(i3 != i2), + "Equality on copy-constructed icursor_iterator is broken."); + PQXX_CHECK( + not(i3 > i2) and not(i3 < i2) and (i3 <= i2) and (i3 >= i2), + "Comparison on identical icursor_iterators is broken."); + + i3 += InitialSkip; + + PQXX_CHECK(not(i3 <= i2), "icursor_iterator operator<=() is broken."); + + pqxx::icursor_iterator iend, i4; + PQXX_CHECK(i3 != iend, "Early end to icursor_iterator iteration."); + i4 = iend; + PQXX_CHECK(i4 == iend, "Assigning empty icursor_iterator fails."); + + // Now start testing our new Cursor. + C >> R; + i2 = i3; + pqxx::result R2(*i2++); + + PQXX_CHECK_EQUAL( + std::size(R), static_cast(GetRows), + "Got unexpected number of rows."); + + PQXX_CHECK_EQUAL(R, R2, "Unexpected result at [1]"); + + C.get(R); + R2 = *i2; + PQXX_CHECK_EQUAL(R, R2, "Unexpected result at [2]"); + i2 += 1; + + C.ignore(GetRows); + C.get(R); + R2 = *++i2; + + PQXX_CHECK_EQUAL(R, R2, "Unexpected result at [3]"); + + ++i2; + R2 = *i2++; + for (int i{1}; C.get(R) and i2 != iend; R2 = *i2++, ++i) + PQXX_CHECK_EQUAL( + R, R2, "Unexpected result in iteration at " + pqxx::to_string(i)); + + PQXX_CHECK(i2 == iend, "Adopted cursor terminated early."); + PQXX_CHECK(not(C >> R), "icursor_iterator terminated early."); +} +} // namespace + + +PQXX_REGISTER_TEST(test_084); diff --git a/ext/libpqxx-7.7.3/test/test87.cxx b/ext/libpqxx-7.7.3/test/test87.cxx new file mode 100644 index 000000000..3a9a58b16 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test87.cxx @@ -0,0 +1,83 @@ +#include "pqxx/config-public-compiler.h" +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include + +#include "test_helpers.hxx" + + +// Test program for libpqxx. Send notification to self, and wait on the +// socket's connection for it to come in. In a simple situation you'd use +// connection::await_notification() for this, but that won't let you wait for +// multiple sockets. +namespace +{ +// Sample implementation of notification receiver. +class TestListener final : public pqxx::notification_receiver +{ + bool m_done; + +public: + explicit TestListener(pqxx::connection &conn, std::string Name) : + pqxx::notification_receiver(conn, Name), m_done(false) + {} + + void operator()(std::string const &, int be_pid) override + { + m_done = true; + PQXX_CHECK_EQUAL( + be_pid, conn().backendpid(), + "Notification came from wrong backend process."); + + std::cout << "Received notification: " << channel() << " pid=" << be_pid + << std::endl; + } + + bool done() const { return m_done; } +}; + + +void test_087() +{ + pqxx::connection conn; + + std::string const NotifName{"my notification"}; + TestListener L{conn, NotifName}; + + pqxx::perform([&conn, &L] { + pqxx::work tx{conn}; + tx.exec0("NOTIFY " + tx.quote_name(L.channel())); + tx.commit(); + }); + + int notifs{0}; + for (int i{0}; (i < 20) and not L.done(); ++i) + { + PQXX_CHECK_EQUAL(notifs, 0, "Got unexpected notifications."); + + std::cout << "."; + + pqxx::internal::wait_fd(conn.sock(), true, false); + notifs = conn.get_notifs(); + } + std::cout << std::endl; + + PQXX_CHECK(L.done(), "No notification received."); + PQXX_CHECK_EQUAL(notifs, 1, "Got unexpected number of notifications."); +} +} // namespace + + +PQXX_REGISTER_TEST(test_087); diff --git a/ext/libpqxx-7.7.3/test/test88.cxx b/ext/libpqxx-7.7.3/test/test88.cxx new file mode 100644 index 000000000..d5deaea75 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test88.cxx @@ -0,0 +1,91 @@ +#include + +#include +#include + +#include "test_helpers.hxx" + + +// Test program for libpqxx. Attempt to perform nested transactions. +namespace +{ +void test_088() +{ + pqxx::connection conn; + + pqxx::work tx0{conn}; + pqxx::test::create_pqxxevents(tx0); + + // Trivial test: create subtransactions, and commit/abort + std::cout << tx0.exec1("SELECT 'tx0 starts'")[0].c_str() << std::endl; + + pqxx::subtransaction T0a(static_cast(tx0), "T0a"); + T0a.commit(); + + pqxx::subtransaction T0b(static_cast(tx0), "T0b"); + T0b.abort(); + std::cout << tx0.exec1("SELECT 'tx0 ends'")[0].c_str() << std::endl; + tx0.commit(); + + // Basic functionality: perform query in subtransaction; abort, continue + pqxx::work tx1{conn, "tx1"}; + std::cout << tx1.exec1("SELECT 'tx1 starts'")[0].c_str() << std::endl; + pqxx::subtransaction tx1a{tx1, "tx1a"}; + std::cout << tx1a.exec1("SELECT ' a'")[0].c_str() << std::endl; + tx1a.commit(); + pqxx::subtransaction tx1b{tx1, "tx1b"}; + std::cout << tx1b.exec1("SELECT ' b'")[0].c_str() << std::endl; + tx1b.abort(); + pqxx::subtransaction tx1c{tx1, "tx1c"}; + std::cout << tx1c.exec1("SELECT ' c'")[0].c_str() << std::endl; + tx1c.commit(); + std::cout << tx1.exec1("SELECT 'tx1 ends'")[0].c_str() << std::endl; + tx1.commit(); + + // Commit/rollback functionality + pqxx::work tx2{conn, "tx2"}; + std::string const Table{"test088"}; + tx2.exec0("CREATE TEMP TABLE " + Table + "(no INTEGER, text VARCHAR)"); + + tx2.exec0("INSERT INTO " + Table + " VALUES(1,'tx2')"); + + pqxx::subtransaction tx2a{tx2, "tx2a"}; + tx2a.exec0("INSERT INTO " + Table + " VALUES(2,'tx2a')"); + tx2a.commit(); + pqxx::subtransaction tx2b{tx2, "tx2b"}; + tx2b.exec0("INSERT INTO " + Table + " VALUES(3,'tx2b')"); + tx2b.abort(); + pqxx::subtransaction tx2c{tx2, "tx2c"}; + tx2c.exec0("INSERT INTO " + Table + " VALUES(4,'tx2c')"); + tx2c.commit(); + auto const R{tx2.exec("SELECT * FROM " + Table + " ORDER BY no")}; + for (auto const &i : R) + std::cout << '\t' << i[0].c_str() << '\t' << i[1].c_str() << std::endl; + + PQXX_CHECK_EQUAL(std::size(R), 3, "Wrong number of results."); + + int expected[3]{1, 2, 4}; + for (pqxx::result::size_type n{0}; n < std::size(R); ++n) + PQXX_CHECK_EQUAL( + R[n][0].as(), expected[n], "Hit unexpected row number."); + + tx2.abort(); + + // Auto-abort should only roll back the subtransaction. + pqxx::work tx3{conn, "tx3"}; + pqxx::subtransaction tx3a(tx3, "tx3a"); + PQXX_CHECK_THROWS( + tx3a.exec("SELECT * FROM nonexistent_table WHERE nonattribute=0"), + pqxx::sql_error, "Bogus query did not fail."); + + // Subtransaction can only be aborted now, because there was an error. + tx3a.abort(); + // We're back in our top-level transaction. This did not abort. + tx3.exec1("SELECT count(*) FROM pqxxevents"); + // Make sure we can commit exactly one more level of transaction. + tx3.commit(); +} +} // namespace + + +PQXX_REGISTER_TEST(test_088); diff --git a/ext/libpqxx-7.7.3/test/test89.cxx b/ext/libpqxx-7.7.3/test/test89.cxx new file mode 100644 index 000000000..2b96dfcd2 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test89.cxx @@ -0,0 +1,44 @@ +#include + +#include +#include + +#include "test_helpers.hxx" + +// Test program for libpqxx. Attempt to perform nested queries on various +// types of connections. +namespace +{ +void test_089() +{ + pqxx::connection C; + + // Trivial test: create subtransactions, and commit/abort + pqxx::work T0(C, "T0"); + T0.exec1("SELECT 'T0 starts'"); + pqxx::subtransaction T0a(T0, "T0a"); + T0a.commit(); + pqxx::subtransaction T0b(T0, "T0b"); + T0b.abort(); + T0.exec1("SELECT 'T0 ends'"); + T0.commit(); + + // Basic functionality: perform query in subtransaction; abort, continue + pqxx::work T1(C, "T1"); + T1.exec1("SELECT 'T1 starts'"); + pqxx::subtransaction T1a(T1, "T1a"); + T1a.exec1("SELECT ' a'"); + T1a.commit(); + pqxx::subtransaction T1b(T1, "T1b"); + T1b.exec1("SELECT ' b'"); + T1b.abort(); + pqxx::subtransaction T1c(T1, "T1c"); + T1c.exec1("SELECT ' c'"); + T1c.commit(); + T1.exec1("SELECT 'T1 ends'"); + T1.commit(); +} +} // namespace + + +PQXX_REGISTER_TEST(test_089); diff --git a/ext/libpqxx-7.7.3/test/test90.cxx b/ext/libpqxx-7.7.3/test/test90.cxx new file mode 100644 index 000000000..d7d97f9f3 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test90.cxx @@ -0,0 +1,23 @@ +#include + +#include "test_helpers.hxx" + +// Test program for libpqxx. Test adorn_name. + +namespace +{ +void test_090() +{ + pqxx::connection conn; + + // Test connection's adorn_name() function for uniqueness + std::string const nametest{"basename"}; + + PQXX_CHECK_NOT_EQUAL( + conn.adorn_name(nametest), conn.adorn_name(nametest), + "\"Unique\" names are not unique."); +} +} // namespace + + +PQXX_REGISTER_TEST(test_090); diff --git a/ext/libpqxx-7.7.3/test/test_helpers.hxx b/ext/libpqxx-7.7.3/test/test_helpers.hxx new file mode 100644 index 000000000..6db3ab971 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test_helpers.hxx @@ -0,0 +1,305 @@ +#include +#include + +#include +#include + +namespace pqxx +{ +namespace test +{ +class test_failure : public std::logic_error +{ + std::string const m_file; + int m_line; + +public: + test_failure(std::string const &ffile, int fline, std::string const &desc); + + ~test_failure() noexcept override; + + std::string const &file() const noexcept { return m_file; } + int line() const noexcept { return m_line; } +}; + + +/// Drop a table, if it exists. +void drop_table(transaction_base &, std::string const &table); + + +using testfunc = void (*)(); + + +void register_test(char const name[], testfunc func); + + +/// Register a test while not inside a function. +struct registrar +{ + registrar(char const name[], testfunc func) + { + pqxx::test::register_test(name, func); + } +}; + + +// Register a test function, so the runner will run it. +#define PQXX_REGISTER_TEST(func) \ + pqxx::test::registrar tst_##func { #func, func } + + +// Unconditional test failure. +#define PQXX_CHECK_NOTREACHED(desc) \ + pqxx::test::check_notreached(__FILE__, __LINE__, (desc)) +[[noreturn]] void +check_notreached(char const file[], int line, std::string desc); + +// Verify that a condition is met, similar to assert() +#define PQXX_CHECK(condition, desc) \ + pqxx::test::check(__FILE__, __LINE__, (condition), #condition, (desc)) +void check( + char const file[], int line, bool condition, char const text[], + std::string const &desc); + +// Verify that variable has the expected value. +#define PQXX_CHECK_EQUAL(actual, expected, desc) \ + pqxx::test::check_equal( \ + __FILE__, __LINE__, (actual), #actual, (expected), #expected, (desc)) +template +inline void check_equal( + char const file[], int line, ACTUAL actual, char const actual_text[], + EXPECTED expected, char const expected_text[], std::string const &desc) +{ + if (expected == actual) + return; + std::string const fulldesc = desc + " (" + actual_text + " <> " + + expected_text + + ": " + "actual=" + + to_string(actual) + + ", " + "expected=" + + to_string(expected) + ")"; + throw test_failure(file, line, fulldesc); +} + +// Verify that two values are not equal. +#define PQXX_CHECK_NOT_EQUAL(value1, value2, desc) \ + pqxx::test::check_not_equal( \ + __FILE__, __LINE__, (value1), #value1, (value2), #value2, (desc)) +template +inline void check_not_equal( + char const file[], int line, VALUE1 value1, char const text1[], + VALUE2 value2, char const text2[], std::string const &desc) +{ + if (value1 != value2) + return; + std::string const fulldesc = desc + " (" + text1 + " == " + text2 + + ": " + "both are " + + to_string(value2) + ")"; + throw test_failure(file, line, fulldesc); +} + + +// Verify that value1 is less than value2. +#define PQXX_CHECK_LESS(value1, value2, desc) \ + pqxx::test::check_less( \ + __FILE__, __LINE__, (value1), #value1, (value2), #value2, (desc)) +// Verify that value1 is greater than value2. +#define PQXX_CHECK_GREATER(value2, value1, desc) \ + pqxx::test::check_less( \ + __FILE__, __LINE__, (value1), #value1, (value2), #value2, (desc)) +template +inline void check_less( + char const file[], int line, VALUE1 value1, char const text1[], + VALUE2 value2, char const text2[], std::string const &desc) +{ + if (value1 < value2) + return; + std::string const fulldesc = desc + " (" + text1 + " >= " + text2 + + ": " + "\"lower\"=" + + to_string(value1) + + ", " + "\"upper\"=" + + to_string(value2) + ")"; + throw test_failure(file, line, fulldesc); +} + + +// Verify that value1 is less than or equal to value2. +#define PQXX_CHECK_LESS_EQUAL(value1, value2, desc) \ + pqxx::test::check_less_equal( \ + __FILE__, __LINE__, (value1), #value1, (value2), #value2, (desc)) +// Verify that value1 is greater than or equal to value2. +#define PQXX_CHECK_GREATER_EQUAL(value2, value1, desc) \ + pqxx::test::check_less_equal( \ + __FILE__, __LINE__, (value1), #value1, (value2), #value2, (desc)) +template +inline void check_less_equal( + char const file[], int line, VALUE1 value1, char const text1[], + VALUE2 value2, char const text2[], std::string const &desc) +{ + if (value1 <= value2) + return; + std::string const fulldesc = desc + " (" + text1 + " > " + text2 + + ": " + "\"lower\"=" + + to_string(value1) + + ", " + "\"upper\"=" + + to_string(value2) + ")"; + throw test_failure(file, line, fulldesc); +} + + +struct failure_to_fail +{}; + + +namespace internal +{ +/// Syntactic placeholder: require (and accept) semicolon after block. +inline void end_of_statement() {} +} // namespace internal + + +// Verify that "action" does not throw an exception. +#define PQXX_CHECK_SUCCEEDS(action, desc) \ + { \ + try \ + { \ + action; \ + } \ + catch (std::exception const &e) \ + { \ + PQXX_CHECK_NOTREACHED( \ + std::string{desc} + " - \"" + \ + #action "\" threw exception: " + e.what()); \ + } \ + catch (...) \ + { \ + PQXX_CHECK_NOTREACHED( \ + std::string{desc} + " - \"" + #action "\" threw a non-exception!"); \ + } \ + } \ + pqxx::test::internal::end_of_statement() + +// Verify that "action" throws an exception, of any std::exception-based type. +#define PQXX_CHECK_THROWS_EXCEPTION(action, desc) \ + { \ + try \ + { \ + action; \ + throw pqxx::test::failure_to_fail(); \ + } \ + catch (pqxx::test::failure_to_fail const &) \ + { \ + PQXX_CHECK_NOTREACHED( \ + std::string{desc} + " (\"" #action "\" did not throw)"); \ + } \ + catch (std::exception const &) \ + {} \ + catch (...) \ + { \ + PQXX_CHECK_NOTREACHED( \ + std::string{desc} + " (\"" #action "\" threw non-exception type)"); \ + } \ + } \ + pqxx::test::internal::end_of_statement() + +// Verify that "action" throws "exception_type" (which is not std::exception). +#define PQXX_CHECK_THROWS(action, exception_type, desc) \ + { \ + try \ + { \ + action; \ + throw pqxx::test::failure_to_fail(); \ + } \ + catch (pqxx::test::failure_to_fail const &) \ + { \ + PQXX_CHECK_NOTREACHED( \ + std::string{desc} + " (\"" #action \ + "\" did not throw " #exception_type ")"); \ + } \ + catch (exception_type const &) \ + {} \ + catch (std::exception const &e) \ + { \ + PQXX_CHECK_NOTREACHED( \ + std::string{desc} + \ + " (\"" #action \ + "\" " \ + "threw exception other than " #exception_type ": " + \ + e.what() + ")"); \ + } \ + catch (...) \ + { \ + PQXX_CHECK_NOTREACHED( \ + std::string{desc} + " (\"" #action "\" threw non-exception type)"); \ + } \ + } \ + pqxx::test::internal::end_of_statement() + +#define PQXX_CHECK_BOUNDS(value, lower, upper, desc) \ + pqxx::test::check_bounds( \ + __FILE__, __LINE__, (value), #value, (lower), #lower, (upper), #upper, \ + (desc)) +template +inline void check_bounds( + char const file[], int line, VALUE value, char const text[], LOWER lower, + char const lower_text[], UPPER upper, char const upper_text[], + std::string const &desc) +{ + std::string const range_check = std::string{lower_text} + " < " + upper_text, + lower_check = + std::string{"!("} + text + " < " + lower_text + ")", + upper_check = std::string{text} + " < " + upper_text; + + pqxx::test::check( + file, line, lower < upper, range_check.c_str(), + desc + " (acceptable range is empty; value was " + text + ")"); + pqxx::test::check( + file, line, not(value < lower), lower_check.c_str(), + desc + " (" + text + " is below lower bound " + lower_text + ")"); + pqxx::test::check( + file, line, value < upper, upper_check.c_str(), + desc + " (" + text + " is not below upper bound " + upper_text + ")"); +} + + +// Report expected exception +void expected_exception(std::string const &); + + +// Represent result row as string. +std::string list_row(row); +// Represent result as string. +std::string list_result(result); +// Represent result iterator as string. +std::string list_result_iterator(result::const_iterator); + + +// @deprecated Set up test data for legacy tests. +void create_pqxxevents(transaction_base &); +} // namespace test + + +template<> inline std::string to_string(row const &value) +{ + return pqxx::test::list_row(value); +} + + +template<> inline std::string to_string(result const &value) +{ + return pqxx::test::list_result(value); +} + + +template<> inline std::string to_string(result::const_iterator const &value) +{ + return pqxx::test::list_result_iterator(value); +} +} // namespace pqxx diff --git a/ext/libpqxx-7.7.3/test/test_types.hxx b/ext/libpqxx-7.7.3/test/test_types.hxx new file mode 100644 index 000000000..38c045370 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/test_types.hxx @@ -0,0 +1,242 @@ +/* + * Custom types for testing & libpqxx support those types + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace pqxx +{ +template<> struct nullness : no_null +{}; + + +constexpr static auto hex_digit{"0123456789abcdef"}; + + +template<> struct string_traits +{ + static std::size_t size_buffer(std::byte const &) { return 3; } + + static zview to_buf(char *begin, char *end, std::byte const &value) + { + if (pqxx::internal::cmp_less(end - begin, size_buffer(value))) + throw pqxx::conversion_overrun{ + "Not enough buffer to convert std::byte."}; + auto uc{static_cast(value)}; + begin[0] = hex_digit[uc >> 4]; + begin[1] = hex_digit[uc & 0x0f]; + return zview{begin, 2u}; + } + + static char *into_buf(char *begin, char *end, std::byte const &value) + { + auto view{to_buf(begin, end, value)}; + return begin + std::size(view); + } +}; +} // namespace pqxx + + +class ipv4 +{ +public: + ipv4() : m_as_int{0u} {} + ipv4(ipv4 const &) = default; + ipv4(ipv4 &&) = default; + explicit ipv4(uint32_t i) : m_as_int{i} {} + ipv4( + unsigned char b1, unsigned char b2, unsigned char b3, unsigned char b4) : + ipv4() + { + set_byte(0, b1); + set_byte(1, b2); + set_byte(2, b3); + set_byte(3, b4); + } + + bool operator==(ipv4 const &o) const { return m_as_int == o.m_as_int; } + ipv4 &operator=(ipv4 const &) = default; + + /// Index bytes, from 0 to 3, in network (i.e. Big-Endian) byte order. + unsigned int operator[](int byte) const + { + if (byte < 0 or byte > 3) + throw pqxx::usage_error("Byte out of range."); + auto const shift = compute_shift(byte); + return static_cast((m_as_int >> shift) & 0xff); + } + + /// Set individual byte, in network byte order. + void set_byte(int byte, uint32_t value) + { + auto const shift = compute_shift(byte); + auto const blanked = (m_as_int & ~uint32_t(0xff << shift)); + m_as_int = (blanked | ((value & 0xff) << shift)); + } + +private: + static unsigned compute_shift(int byte) + { + if (byte < 0 or byte > 3) + throw pqxx::usage_error("Byte out of range."); + return static_cast((3 - byte) * 8); + } + + uint32_t m_as_int; +}; + + +using bytea = std::vector; + + +namespace pqxx +{ +template<> struct nullness : no_null +{}; + + +template<> struct string_traits +{ + static ipv4 from_string(std::string_view text) + { + ipv4 ts; + if (std::data(text) == nullptr) + internal::throw_null_conversion(type_name); + std::regex ipv4_regex{R"--((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}))--"}; + std::smatch match; + // Need non-temporary for `std::regex_match()` + std::string sstr{text}; + if (not std::regex_match(sstr, match, ipv4_regex) or std::size(match) != 5) + throw std::runtime_error{"Invalid ipv4 format: " + std::string{text}}; + try + { + for (std::size_t i{0}; i < 4; ++i) + ts.set_byte(int(i), uint32_t(std::stoi(match[i + 1]))); + } + catch (std::invalid_argument const &) + { + throw std::runtime_error{"Invalid ipv4 format: " + std::string{text}}; + } + catch (std::out_of_range const &) + { + throw std::runtime_error{"Invalid ipv4 format: " + std::string{text}}; + } + return ts; + } + + static char *into_buf(char *begin, char *end, ipv4 const &value) + { + if (pqxx::internal::cmp_less(end - begin, size_buffer(value))) + throw conversion_error{"Buffer too small for ipv4."}; + char *here = begin; + for (int i = 0; i < 4; ++i) + { + here = string_traits::into_buf(here, end, value[i]); + *(here - 1) = '.'; + } + *(here - 1) = '\0'; + return here; + } + + static zview to_buf(char *begin, char *end, ipv4 const &value) + { + return zview{ + begin, + static_cast(into_buf(begin, end, value) - begin - 1)}; + } + + static constexpr std::size_t size_buffer(ipv4 const &) noexcept + { + return 20; + } +}; + + +namespace +{ +inline char nibble_to_hex(unsigned nibble) +{ + if (nibble < 10) + return char('0' + nibble); + else if (nibble < 16) + return char('a' + (nibble - 10)); + else + throw std::runtime_error{"Invalid digit going into bytea."}; +} + + +inline unsigned hex_to_digit(char hex) +{ + auto x = static_cast(hex); + if (x >= '0' and x <= '9') + return x - '0'; + else if (x >= 'a' and x <= 'f') + return 10 + x - 'a'; + else if (x >= 'A' and x <= 'F') + return 10 + x - 'A'; + else + throw std::runtime_error{"Invalid hex in bytea."}; +} +} // namespace + + +template<> struct nullness : no_null +{}; + + +template<> struct string_traits +{ + static bytea from_string(std::string_view text) + { + if ((std::size(text) & 1) != 0) + throw std::runtime_error{"Odd hex size."}; + bytea value; + value.reserve((std::size(text) - 2) / 2); + for (std::size_t i = 2; i < std::size(text); i += 2) + { + auto hi = hex_to_digit(text[i]), lo = hex_to_digit(text[i + 1]); + value.push_back(static_cast((hi << 4) | lo)); + } + return value; + } + + static zview to_buf(char *begin, char *end, bytea const &value) + { + auto const need = size_buffer(value); + auto const have = end - begin; + if (std::size_t(have) < need) + throw pqxx::conversion_overrun{"Not enough space in buffer for bytea."}; + char *pos = begin; + *pos++ = '\\'; + *pos++ = 'x'; + for (unsigned char const u : value) + { + *pos++ = nibble_to_hex(unsigned(u) >> 4); + *pos++ = nibble_to_hex(unsigned(u) & 0x0f); + } + *pos++ = '\0'; + return {begin, pos - begin - 1}; + } + + static char *into_buf(char *begin, char *end, bytea const &value) + { + return begin + std::size(to_buf(begin, end, value)) + 1; + } + + static std::size_t size_buffer(bytea const &value) + { + return 2 + 2 * std::size(value) + 1; + } +}; +} // namespace pqxx diff --git a/ext/libpqxx-7.7.3/test/unit/CMakeLists.txt b/ext/libpqxx-7.7.3/test/unit/CMakeLists.txt new file mode 100644 index 000000000..f938a4181 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/CMakeLists.txt @@ -0,0 +1,25 @@ +if(NOT PostgreSQL_FOUND) + if(POLICY CMP0074) + cmake_policy(PUSH) + # CMP0074 is `OLD` by `cmake_minimum_required(VERSION 3.7)`, sets `NEW` + # to enable support CMake variable `PostgreSQL_ROOT`. + cmake_policy(SET CMP0074 NEW) + endif() + + find_package(PostgreSQL REQUIRED) + + if(POLICY CMP0074) + cmake_policy(POP) + endif() +endif() + +file(GLOB UNIT_TEST_SOURCES *.cxx) + +add_executable(unit_runner ${UNIT_TEST_SOURCES}) +target_link_libraries(unit_runner PUBLIC pqxx) +target_include_directories(unit_runner PRIVATE ${PostgreSQL_INCLUDE_DIRS}) +add_test( + NAME unit_runner + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMAND unit_runner +) diff --git a/ext/libpqxx-7.7.3/test/unit/test_array.cxx b/ext/libpqxx-7.7.3/test/unit/test_array.cxx new file mode 100644 index 000000000..229ac5145 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_array.cxx @@ -0,0 +1,548 @@ +#include + +#include "../test_helpers.hxx" + +// Test program for libpqxx array parsing. + +namespace pqxx +{ +template<> +struct nullness : no_null +{}; + + +inline std::string to_string(pqxx::array_parser::juncture const &j) +{ + using junc = pqxx::array_parser::juncture; + switch (j) + { + case junc::row_start: return "row_start"; + case junc::row_end: return "row_end"; + case junc::null_value: return "null_value"; + case junc::string_value: return "string_value"; + case junc::done: return "done"; + default: return "UNKNOWN JUNCTURE: " + to_string(static_cast(j)); + } +} +} // namespace pqxx + + +namespace +{ +void test_empty_arrays() +{ + std::pair output; + + // Parsing a null pointer just immediately returns "done". + output = pqxx::array_parser(std::string_view()).get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "get_next on null array did not return done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + // Parsing an empty array string immediately returns "done". + output = pqxx::array_parser("").get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "get_next on an empty array string did not return done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + // Parsing an empty array returns "row_start", "row_end", "done". + pqxx::array_parser empty_parser("{}"); + output = empty_parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Empty array did not start with row_start."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = empty_parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Empty array did not end with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = empty_parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "Empty array did not conclude with done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); +} + + +void test_null_value() +{ + std::pair output; + pqxx::array_parser containing_null("{NULL}"); + + output = containing_null.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Array containing null did not start with row_start."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = containing_null.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::null_value, + "Array containing null did not return null_value."); + PQXX_CHECK_EQUAL(output.second, "", "Null value was not empty."); + + output = containing_null.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Array containing null did not end with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = containing_null.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "Array containing null did not conclude with done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); +} + + +void test_single_quoted_string() +{ + std::pair output; + pqxx::array_parser parser("{'item'}"); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Array did not start with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "item", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Array did not end with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "Array did not conclude with done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); +} + + +void test_single_quoted_escaping() +{ + std::pair output; + pqxx::array_parser parser("{'don''t\\\\ care'}"); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Array did not start with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "don't\\ care", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Array did not end with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "Array did not conclude with done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); +} + + +void test_double_quoted_string() +{ + std::pair output; + pqxx::array_parser parser("{\"item\"}"); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Array did not start with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "item", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Array did not end with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "Array did not conclude with done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); +} + + +void test_double_quoted_escaping() +{ + std::pair output; + pqxx::array_parser parser(R"--({"don''t\\ care"})--"); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Array did not start with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "don''t\\ care", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Array did not end with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "Array did not conclude with done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); +} + + +// A pair of double quotes in a double-quoted string is an escaped quote. +void test_double_double_quoted_string() +{ + std::pair output; + pqxx::array_parser parser{R"--({"3"" steel"})--"}; + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Array did not start with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + + PQXX_CHECK_EQUAL(output.second, "3\" steel", "Unexpected string value."); +} + + +void test_unquoted_string() +{ + std::pair output; + pqxx::array_parser parser("{item}"); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Array did not start with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "item", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Array did not end with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "Array did not conclude with done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); +} + + +void test_multiple_values() +{ + std::pair output; + pqxx::array_parser parser("{1,2}"); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Array did not start with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "1", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "2", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Array did not end with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "Array did not conclude with done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); +} + + +void test_nested_array() +{ + std::pair output; + pqxx::array_parser parser("{{item}}"); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Array did not start with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Nested array did not start 2nd dimension with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "item", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Nested array did not end 2nd dimension with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Array did not end with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "Array did not conclude with done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); +} + + +void test_nested_array_with_multiple_entries() +{ + std::pair output; + pqxx::array_parser parser("{{1,2},{3,4}}"); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Array did not start with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Nested array did not start 2nd dimension with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "1", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "2", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Nested array did not end 2nd dimension with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_start, + "Nested array did not descend to 2nd dimension with row_start."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "3", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::string_value, + "Array did not return string_value."); + PQXX_CHECK_EQUAL(output.second, "4", "Unexpected string value."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Nested array did not leave 2nd dimension with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::row_end, + "Array did not end with row_end."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); + + output = parser.get_next(); + PQXX_CHECK_EQUAL( + output.first, pqxx::array_parser::juncture::done, + "Array did not conclude with done."); + PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output."); +} + + +void test_array_parse() +{ + test_empty_arrays(); + test_null_value(); + test_single_quoted_string(); + test_single_quoted_escaping(); + test_double_quoted_string(); + test_double_quoted_escaping(); + test_double_double_quoted_string(); + test_unquoted_string(); + test_multiple_values(); + test_nested_array(); + test_nested_array_with_multiple_entries(); +} + + +void test_generate_empty_array() +{ + PQXX_CHECK_EQUAL( + pqxx::to_string(std::vector{}), "{}", + "Basic array output is not as expected."); + PQXX_CHECK_EQUAL( + pqxx::to_string(std::vector{}), "{}", + "String array comes out different."); +} + + +void test_generate_null_value() +{ + PQXX_CHECK_EQUAL( + pqxx::to_string(std::vector{nullptr}), "{NULL}", + "Null array value did not come out as expected."); +} + + +void test_generate_single_item() +{ + PQXX_CHECK_EQUAL( + pqxx::to_string(std::vector{42}), "{42}", + "Numeric conversion came out wrong."); + + PQXX_CHECK_EQUAL( + pqxx::to_string(std::vector{"foo"}), "{\"foo\"}", + "String array conversion came out wrong."); +} + + +void test_generate_multiple_items() +{ + PQXX_CHECK_EQUAL( + pqxx::to_string(std::vector{5, 4, 3, 2}), "{5,4,3,2}", + "Array with multiple values is not correct."); + PQXX_CHECK_EQUAL( + pqxx::to_string(std::vector{"foo", "bar"}), + "{\"foo\",\"bar\"}", "Array with multiple strings came out wrong."); +} + + +void test_generate_nested_array() +{ + PQXX_CHECK_EQUAL( + pqxx::to_string(std::vector>{{1, 2}, {3, 4}}), + "{{1,2},{3,4}}", "Nested arrays don't work right."); +} + + +void test_generate_escaped_strings() +{ + PQXX_CHECK_EQUAL( + pqxx::to_string(std::vector{"a\\b"}), "{\"a\\\\b\"}", + "Backslashes are not escaped properly."); + PQXX_CHECK_EQUAL( + pqxx::to_string(std::vector{"x\"y\""}), "{\"x\\\"y\\\"\"}", + "Double quotes are not escaped properly."); +} + + +void test_array_generate() +{ + test_generate_empty_array(); + test_generate_null_value(); + test_generate_single_item(); + test_generate_multiple_items(); + test_generate_nested_array(); + test_generate_escaped_strings(); +} + + +void test_array_roundtrip() +{ + pqxx::connection c; + pqxx::work w{c}; + + std::vector const in{0, 1, 2, 3, 5}; + auto const r1{w.exec1("SELECT " + c.quote(in) + "::integer[]")}; + pqxx::array_parser parser{r1[0].view()}; + auto item{parser.get_next()}; + PQXX_CHECK_EQUAL( + item.first, pqxx::array_parser::juncture::row_start, + "Array did not start with row_start."); + + std::vector out; + for (item = parser.get_next(); + item.first == pqxx::array_parser::juncture::string_value; + item = parser.get_next()) + { + out.push_back(pqxx::from_string(item.second)); + } + + PQXX_CHECK_EQUAL( + item.first, pqxx::array_parser::juncture::row_end, + "Array values did not end in row_end."); + PQXX_CHECK_EQUAL( + std::size(out), std::size(in), "Array came back with different length."); + + for (std::size_t i{0}; i < std::size(in); ++i) + PQXX_CHECK_EQUAL(out[i], in[i], "Array element has changed."); + + item = parser.get_next(); + PQXX_CHECK_EQUAL( + item.first, pqxx::array_parser::juncture::done, + "Array did not end in done."); +} + + +PQXX_REGISTER_TEST(test_array_parse); +PQXX_REGISTER_TEST(test_array_generate); +PQXX_REGISTER_TEST(test_array_roundtrip); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_binarystring.cxx b/ext/libpqxx-7.7.3/test/unit/test_binarystring.cxx new file mode 100644 index 000000000..e6097d039 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_binarystring.cxx @@ -0,0 +1,211 @@ +#include +#include +#include + +#include "../test_helpers.hxx" +#include "../test_types.hxx" + + +namespace +{ +pqxx::binarystring +make_binarystring(pqxx::transaction_base &T, std::string content) +{ +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return pqxx::binarystring(T.exec1("SELECT " + T.quote_raw(content))[0]); +#include "pqxx/internal/ignore-deprecated-post.hxx" +} + + +void test_binarystring() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + auto b{make_binarystring(tx, "")}; + PQXX_CHECK(std::empty(b), "Empty binarystring is not empty."); + PQXX_CHECK_EQUAL(b.str(), "", "Empty binarystring doesn't work."); + PQXX_CHECK_EQUAL(std::size(b), 0u, "Empty binarystring has nonzero size."); + PQXX_CHECK_EQUAL(b.length(), 0u, "Length/size mismatch."); + PQXX_CHECK(std::begin(b) == std::end(b), "Empty binarystring iterates."); + PQXX_CHECK( + std::cbegin(b) == std::begin(b), "Wrong cbegin for empty binarystring."); + PQXX_CHECK( + std::rbegin(b) == std::rend(b), "Empty binarystring reverse-iterates."); + PQXX_CHECK( + std::crbegin(b) == std::rbegin(b), + "Wrong crbegin for empty binarystring."); + PQXX_CHECK_THROWS( + b.at(0), std::out_of_range, "Empty binarystring accepts at()."); + + b = make_binarystring(tx, "z"); + PQXX_CHECK_EQUAL(b.str(), "z", "Basic nonempty binarystring is broken."); + PQXX_CHECK(not std::empty(b), "Nonempty binarystring is empty."); + PQXX_CHECK_EQUAL(std::size(b), 1u, "Bad binarystring size."); + PQXX_CHECK_EQUAL(b.length(), 1u, "Length/size mismatch."); + PQXX_CHECK( + std::begin(b) != std::end(b), "Nonempty binarystring does not iterate."); + PQXX_CHECK( + std::rbegin(b) != std::rend(b), + "Nonempty binarystring does not reverse-iterate."); + PQXX_CHECK(std::begin(b) + 1 == std::end(b), "Bad iteration."); + PQXX_CHECK(std::rbegin(b) + 1 == std::rend(b), "Bad reverse iteration."); + PQXX_CHECK(std::cbegin(b) == std::begin(b), "Wrong cbegin."); + PQXX_CHECK(std::cend(b) == std::end(b), "Wrong cend."); + PQXX_CHECK(std::crbegin(b) == std::rbegin(b), "Wrong crbegin."); + PQXX_CHECK(std::crend(b) == std::rend(b), "Wrong crend."); + PQXX_CHECK(b.front() == 'z', "Unexpected front()."); + PQXX_CHECK(b.back() == 'z', "Unexpected back()."); + PQXX_CHECK(b.at(0) == 'z', "Unexpected data at index 0."); + PQXX_CHECK_THROWS( + b.at(1), std::out_of_range, "Failed to catch range error."); + + std::string const simple{"ab"}; + b = make_binarystring(tx, simple); + PQXX_CHECK_EQUAL( + b.str(), simple, "Binary (un)escaping went wrong somewhere."); + PQXX_CHECK_EQUAL( + std::size(b), std::size(simple), "Escaping confuses length."); + + std::string const simple_escaped{ + tx.esc_raw(std::basic_string_view{ + reinterpret_cast(std::data(simple)), + std::size(simple)})}; + for (auto c : simple_escaped) + { + auto const uc{static_cast(c)}; + PQXX_CHECK(uc <= 127, "Non-ASCII byte in escaped string."); + } + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + PQXX_CHECK_EQUAL( + tx.quote_raw( + reinterpret_cast(simple.c_str()), + std::size(simple)), + tx.quote(b), "quote_raw is broken"); + PQXX_CHECK_EQUAL( + tx.quote(b), tx.quote_raw(simple), "Binary quoting is broken."); + PQXX_CHECK_EQUAL( + pqxx::binarystring(tx.exec1("SELECT " + tx.quote(b))[0]).str(), simple, + "Binary string is not idempotent."); +#include "pqxx/internal/ignore-deprecated-post.hxx" + + std::string const bytes("\x01\x23\x23\xa1\x2b\x0c\xff"); + b = make_binarystring(tx, bytes); + PQXX_CHECK_EQUAL(b.str(), bytes, "Binary data breaks (un)escaping."); + + std::string const nully("a\0b", 3); + b = make_binarystring(tx, nully); + PQXX_CHECK_EQUAL(b.str(), nully, "Nul byte broke binary (un)escaping."); + PQXX_CHECK_EQUAL(std::size(b), 3u, "Nul byte broke binarystring size."); + + b = make_binarystring(tx, "foo"); + PQXX_CHECK_EQUAL(std::string(b.get(), 3), "foo", "get() appears broken."); + + auto b1{make_binarystring(tx, "1")}, b2{make_binarystring(tx, "2")}; + PQXX_CHECK_NOT_EQUAL(b1.get(), b2.get(), "Madness rules."); + PQXX_CHECK_NOT_EQUAL(b1.str(), b2.str(), "Logic has no more meaning."); + b1.swap(b2); + PQXX_CHECK_NOT_EQUAL(b1.str(), b2.str(), "swap() equalized binarystrings."); + PQXX_CHECK_NOT_EQUAL(b1.str(), "1", "swap() did not happen."); + PQXX_CHECK_EQUAL(b1.str(), "2", "swap() is broken."); + PQXX_CHECK_EQUAL(b2.str(), "1", "swap() went insane."); + + b = make_binarystring(tx, "bar"); + b.swap(b); + PQXX_CHECK_EQUAL(b.str(), "bar", "Self-swap confuses binarystring."); + + b = make_binarystring(tx, "\\x"); + PQXX_CHECK_EQUAL(b.str(), "\\x", "Hex-escape header confused (un)escaping."); +} + + +void test_binarystring_conversion() +{ + constexpr char bytes[]{"f\to\0o\n\0"}; + std::string_view const data{bytes, std::size(bytes) - 1}; +#include "pqxx/internal/ignore-deprecated-pre.hxx" + pqxx::binarystring bin{data}; +#include "pqxx/internal/ignore-deprecated-post.hxx" + auto const escaped{pqxx::to_string(bin)}; + PQXX_CHECK_EQUAL( + escaped, std::string_view{"\\x66096f006f0a00"}, "Unexpected hex escape."); + auto const restored{pqxx::from_string(escaped)}; + PQXX_CHECK_EQUAL( + std::size(restored), std::size(data), "Unescaping produced wrong length."); +} + + +void test_binarystring_stream() +{ + constexpr char bytes[]{"a\tb\0c"}; + std::string_view const data{bytes, std::size(bytes) - 1}; +#include "pqxx/internal/ignore-deprecated-pre.hxx" + pqxx::binarystring bin{data}; +#include "pqxx/internal/ignore-deprecated-post.hxx" + + pqxx::connection conn; + pqxx::transaction tx{conn}; + tx.exec0("CREATE TEMP TABLE pqxxbinstream(id integer, bin bytea)"); + + auto to{pqxx::stream_to::table(tx, {"pqxxbinstream"})}; + to.write_values(0, bin); + to.complete(); + + auto ptr{reinterpret_cast(std::data(data))}; + auto expect{ + tx.quote(std::basic_string_view{ptr, std::size(data)})}; + PQXX_CHECK( + tx.query_value("SELECT bin = " + expect + " FROM pqxxbinstream"), + "binarystring did not stream_to properly."); + PQXX_CHECK_EQUAL( + tx.query_value("SELECT octet_length(bin) FROM pqxxbinstream"), + std::size(data), "Did the terminating zero break the bytea?"); +} + + +void test_binarystring_array_stream() +{ + pqxx::connection conn; + pqxx::transaction tx{conn}; + tx.exec0("CREATE TEMP TABLE pqxxbinstream(id integer, vec bytea[])"); + + constexpr char bytes1[]{"a\tb\0c"}, bytes2[]{"1\0.2"}; + std::string_view const data1{bytes1}, data2{bytes2}; +#include "pqxx/internal/ignore-deprecated-pre.hxx" + pqxx::binarystring bin1{data1}, bin2{data2}; + std::vector const vec{bin1, bin2}; +#include "pqxx/internal/ignore-deprecated-post.hxx" + + auto to{pqxx::stream_to::table(tx, {"pqxxbinstream"})}; + to.write_values(0, vec); + to.complete(); + + PQXX_CHECK_EQUAL( + tx.query_value( + "SELECT array_length(vec, 1) FROM pqxxbinstream"), + std::size(vec), "Array came out with wrong length."); + + auto ptr1{reinterpret_cast(std::data(data1))}, + ptr2{reinterpret_cast(std::data(data2))}; + auto expect1{ + tx.quote(std::basic_string_view{ptr1, std::size(data1)})}, + expect2{ + tx.quote(std::basic_string_view{ptr2, std::size(data2)})}; + PQXX_CHECK( + tx.query_value("SELECT vec[1] = " + expect1 + " FROM pqxxbinstream"), + "Bytea in array came out wrong."); + PQXX_CHECK( + tx.query_value("SELECT vec[2] = " + expect2 + " FROM pqxxbinstream"), + "First bytea in array worked, but second did not."); + PQXX_CHECK_EQUAL( + tx.query_value( + "SELECT octet_length(vec[1]) FROM pqxxbinstream"), + std::size(data1), "Bytea length broke inside array."); +} + + +PQXX_REGISTER_TEST(test_binarystring); +PQXX_REGISTER_TEST(test_binarystring_conversion); +PQXX_REGISTER_TEST(test_binarystring_stream); +PQXX_REGISTER_TEST(test_binarystring_array_stream); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_blob.cxx b/ext/libpqxx-7.7.3/test/unit/test_blob.cxx new file mode 100644 index 000000000..709c1f489 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_blob.cxx @@ -0,0 +1,644 @@ +#include + +#include +#include + +#include "../test_helpers.hxx" +#include "../test_types.hxx" + + +namespace +{ +void test_blob_is_useless_by_default() +{ + pqxx::blob b{}; + std::basic_string buf; + PQXX_CHECK_THROWS( + b.read(buf, 1), pqxx::usage_error, + "Read on default-constructed blob did not throw failure."); + PQXX_CHECK_THROWS( + b.write(buf), pqxx::usage_error, + "Write on default-constructed blob did not throw failure."); +} + + +void test_blob_create_makes_empty_blob() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::oid id{pqxx::blob::create(tx)}; + auto b{pqxx::blob::open_r(tx, id)}; + b.seek_end(0); + PQXX_CHECK_EQUAL(b.tell(), 0, "New blob is not empty."); +} + + +void test_blob_create_with_oid_requires_oid_be_free() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{pqxx::blob::create(tx)}; + + PQXX_CHECK_THROWS( + pqxx::ignore_unused(pqxx::blob::create(tx, id)), pqxx::failure, + "Not getting expected error when oid not free."); +} + + +void test_blob_create_with_oid_obeys_oid() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{pqxx::blob::create(tx)}; + pqxx::blob::remove(tx, id); + + auto actual_id{pqxx::blob::create(tx, id)}; + PQXX_CHECK_EQUAL(actual_id, id, "Create with oid returned different oid."); +} + + +void test_blobs_are_transactional() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::oid id{pqxx::blob::create(tx)}; + tx.abort(); + pqxx::work tx2{conn}; + PQXX_CHECK_THROWS( + pqxx::ignore_unused(pqxx::blob::open_r(tx2, id)), pqxx::failure, + "Blob from aborted transaction still exists."); +} + + +void test_blob_remove_removes_blob() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::oid id{pqxx::blob::create(tx)}; + pqxx::blob::remove(tx, id); + PQXX_CHECK_THROWS( + pqxx::ignore_unused(pqxx::blob::open_r(tx, id)), pqxx::failure, + "Attempt to open blob after removing should have failed."); +} + + +void test_blob_remove_is_not_idempotent() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::oid id{pqxx::blob::create(tx)}; + pqxx::blob::remove(tx, id); + PQXX_CHECK_THROWS( + pqxx::blob::remove(tx, id), pqxx::failure, + "Redundant remove() did not throw failure."); +} + + +void test_blob_checks_open_mode() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::oid id{pqxx::blob::create(tx)}; + pqxx::blob b_r{pqxx::blob::open_r(tx, id)}; + pqxx::blob b_w{pqxx::blob::open_w(tx, id)}; + pqxx::blob b_rw{pqxx::blob::open_rw(tx, id)}; + + std::basic_string buf{std::byte{3}, std::byte{2}, std::byte{1}}; + + // These are all allowed: + b_w.write(buf); + b_r.read(buf, 3); + b_rw.seek_end(0); + b_rw.write(buf); + b_rw.seek_abs(0); + b_rw.read(buf, 6); + + // These are not: + PQXX_CHECK_THROWS( + b_r.write(buf), pqxx::failure, "Read-only blob did not stop write."); + PQXX_CHECK_THROWS( + b_w.read(buf, 10), pqxx::failure, "Write-only blob did not stop read."); +} + + +void test_blob_supports_move() +{ + std::basic_string buf; + buf.push_back(std::byte{'x'}); + + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::oid id{pqxx::blob::create(tx)}; + pqxx::blob b1{pqxx::blob::open_rw(tx, id)}; + b1.write(buf); + + pqxx::blob b2{std::move(b1)}; + b2.seek_abs(0); + b2.read(buf, 1u); + + PQXX_CHECK_THROWS( + b1.read(buf, 1u), pqxx::usage_error, + "Blob still works after move construction."); + + b1 = std::move(b2); + b1.read(buf, 1u); + + PQXX_CHECK_THROWS( + b2.read(buf, 1u), pqxx::usage_error, + "Blob still works after move assignment."); +} + + +void test_blob_read_reads_data() +{ + std::basic_string const data{ + std::byte{'a'}, std::byte{'b'}, std::byte{'c'}}; + + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::oid id{pqxx::blob::from_buf(tx, data)}; + + std::basic_string buf; + auto b{pqxx::blob::open_rw(tx, id)}; + PQXX_CHECK_EQUAL( + b.read(buf, 2), 2u, "Full read() returned an unexpected value."); + PQXX_CHECK_EQUAL( + buf, (std::basic_string{std::byte{'a'}, std::byte{'b'}}), + "Read back the wrong data."); + PQXX_CHECK_EQUAL( + b.read(buf, 2), 1u, "Partial read() returned an unexpected value."); + PQXX_CHECK_EQUAL( + buf, (std::basic_string{std::byte{'c'}}), + "Continued read produced wrong data."); + PQXX_CHECK_EQUAL( + b.read(buf, 2), 0u, "read at end returned an unexpected value."); + PQXX_CHECK_EQUAL( + buf, (std::basic_string{}), "Read past end produced data."); +} + + +void test_blob_read_span() +{ +#if defined(PQXX_HAVE_SPAN) + std::basic_string const data{std::byte{'u'}, std::byte{'v'}, + std::byte{'w'}, std::byte{'x'}, + std::byte{'y'}, std::byte{'z'}}; + + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::oid id{pqxx::blob::from_buf(tx, data)}; + + auto b{pqxx::blob::open_r(tx, id)}; + std::basic_string string_buf; + string_buf.resize(2); + + std::span output; + + output = b.read(std::span{}); + PQXX_CHECK_EQUAL( + std::size(output), 0u, "Empty read produced nonempty buffer."); + output = b.read(string_buf); + PQXX_CHECK_EQUAL( + std::size(output), 2u, "Got unexpected buf size from blob::read()."); + PQXX_CHECK_EQUAL( + output[0], std::byte{'u'}, "Unexpected byte from blob::read()."); + PQXX_CHECK_EQUAL( + output[1], std::byte{'v'}, "Unexpected byte from blob::read()."); + + string_buf.resize(100); + output = b.read(std::span{string_buf.data(), 1}); + PQXX_CHECK_EQUAL( + std::size(output), 1u, + "Did blob::read() follow string size instead of span size?"); + PQXX_CHECK_EQUAL( + output[0], std::byte{'w'}, "Unexpected byte from blob::read()."); + + std::vector vec_buf; + vec_buf.resize(2); + auto output2{b.read(vec_buf)}; + PQXX_CHECK_EQUAL( + std::size(output2), 2u, "Got unexpected buf size from blob::read()."); + PQXX_CHECK_EQUAL( + output2[0], std::byte{'x'}, "Unexpected byte from blob::read()."); + PQXX_CHECK_EQUAL( + output2[1], std::byte{'y'}, "Unexpected byte from blob::read()."); + + vec_buf.resize(100); + output2 = b.read(vec_buf); + PQXX_CHECK_EQUAL(std::size(output2), 1u, "Weird things happened at EOF."); + PQXX_CHECK_EQUAL(output2[0], std::byte{'z'}, "Bad data at EOF."); +#endif // PQXX_HAVE_SPAN +} + + +void test_blob_reads_vector() +{ + char const content[]{"abcd"}; + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{pqxx::blob::from_buf( + tx, std::basic_string_view{ + reinterpret_cast(content), std::size(content)})}; + std::vector buf; + buf.resize(10); + auto out{pqxx::blob::open_r(tx, id).read(buf)}; + PQXX_CHECK_EQUAL( + std::size(out), std::size(content), + "Got wrong length back when reading as vector."); + PQXX_CHECK_EQUAL( + out[0], std::byte{'a'}, "Got bad data when reading as vector."); +} + + +void test_blob_write_appends_at_insertion_point() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{pqxx::blob::create(tx)}; + + auto b{pqxx::blob::open_rw(tx, id)}; + b.write(std::basic_string{std::byte{'z'}}); + b.write(std::basic_string{std::byte{'a'}}); + + std::basic_string buf; + b.read(buf, 5); + PQXX_CHECK_EQUAL( + buf, (std::basic_string{}), "Found data at the end."); + b.seek_abs(0); + b.read(buf, 5); + PQXX_CHECK_EQUAL( + buf, (std::basic_string{std::byte{'z'}, std::byte{'a'}}), + "Consecutive writes did not append correctly."); + + b.write(std::basic_string{std::byte{'x'}}); + // Blob now contains "zax". That's not we wanted... Rewind and rewrite. + b.seek_abs(1); + b.write(std::basic_string{std::byte{'y'}}); + b.seek_abs(0); + b.read(buf, 5); + PQXX_CHECK_EQUAL( + buf, + (std::basic_string{ + std::byte{'z'}, std::byte{'y'}, std::byte{'x'}}), + "Rewriting in the middle did not work right."); +} + + +void test_blob_writes_span() +{ +#if defined(PQXX_HAVE_SPAN) + pqxx::connection conn; + pqxx::work tx{conn}; + constexpr char content[]{"gfbltk"}; + std::basic_string data{ + reinterpret_cast(content), std::size(content)}; + + auto id{pqxx::blob::create(tx)}; + auto b{pqxx::blob::open_rw(tx, id)}; + b.write(std::span{data.data() + 1, 3u}); + b.seek_abs(0); + + std::vector buf; + buf.resize(4); + auto out{b.read(std::span{buf.data(), 4u})}; + PQXX_CHECK_EQUAL( + std::size(out), 3u, "Did not get expected number of bytes back."); + PQXX_CHECK_EQUAL(out[0], std::byte{'f'}, "Data did not come back right."); + PQXX_CHECK_EQUAL(out[2], std::byte{'l'}, "Data started right, ended wrong!"); +#endif // PQXX_HAVE_SPAN +} + + +void test_blob_resize_shortens_to_desired_length() +{ + std::basic_string const data{ + std::byte{'w'}, std::byte{'o'}, std::byte{'r'}, std::byte{'k'}}; + + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{pqxx::blob::from_buf(tx, data)}; + + pqxx::blob::open_w(tx, id).resize(2); + std::basic_string buf; + pqxx::blob::to_buf(tx, id, buf, 10); + PQXX_CHECK_EQUAL( + buf, (std::basic_string{std::byte{'w'}, std::byte{'o'}}), + "Truncate did not shorten correctly."); +} + + +void test_blob_resize_extends_to_desired_length() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{ + pqxx::blob::from_buf(tx, std::basic_string{std::byte{100}})}; + pqxx::blob::open_w(tx, id).resize(3); + std::basic_string buf; + pqxx::blob::to_buf(tx, id, buf, 10); + PQXX_CHECK_EQUAL( + buf, + (std::basic_string{std::byte{100}, std::byte{0}, std::byte{0}}), + "Resize did not zero-extend correctly."); +} + + +void test_blob_tell_tracks_position() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{pqxx::blob::create(tx)}; + auto b{pqxx::blob::open_rw(tx, id)}; + + PQXX_CHECK_EQUAL( + b.tell(), 0, "Empty blob started out in non-zero position."); + b.write(std::basic_string{std::byte{'e'}, std::byte{'f'}}); + PQXX_CHECK_EQUAL( + b.tell(), 2, "Empty blob started out in non-zero position."); + b.seek_abs(1); + PQXX_CHECK_EQUAL(b.tell(), 1, "tell() did not track seek."); +} + + +void test_blob_seek_sets_positions() +{ + std::basic_string data{ + std::byte{0}, std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, + std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}}; + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{pqxx::blob::from_buf(tx, data)}; + auto b{pqxx::blob::open_r(tx, id)}; + + std::basic_string buf; + b.seek_rel(3); + b.read(buf, 1u); + PQXX_CHECK_EQUAL( + buf[0], std::byte{3}, + "seek_rel() from beginning did not take us to the right position."); + + b.seek_abs(2); + b.read(buf, 1u); + PQXX_CHECK_EQUAL( + buf[0], std::byte{2}, "seek_abs() did not take us to the right position."); + + b.seek_end(-2); + b.read(buf, 1u); + PQXX_CHECK_EQUAL( + buf[0], std::byte{8}, "seek_end() did not take us to the right position."); +} + + +void test_blob_from_buf_interoperates_with_to_buf() +{ + std::basic_string const data{std::byte{'h'}, std::byte{'i'}}; + std::basic_string buf; + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::blob::to_buf(tx, pqxx::blob::from_buf(tx, data), buf, 10); + PQXX_CHECK_EQUAL(buf, data, "from_buf()/to_buf() roundtrip did not work."); +} + + +void test_blob_append_from_buf_appends() +{ + std::basic_string const data{std::byte{'h'}, std::byte{'o'}}; + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{pqxx::blob::create(tx)}; + pqxx::blob::append_from_buf(tx, data, id); + pqxx::blob::append_from_buf(tx, data, id); + std::basic_string buf; + pqxx::blob::to_buf(tx, id, buf, 10); + PQXX_CHECK_EQUAL(buf, data + data, "append_from_buf() wrote wrong data?"); +} + + +namespace +{ +/// Wrap `std::fopen`. +/** This is just here to stop Visual Studio from advertising its own + * alternative. + */ +std::unique_ptr> +my_fopen(char const *path, char const *mode) +{ +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4996) +#endif + return {std::fopen(path, mode), std::fclose}; +#if defined(_MSC_VER) +# pragma warning(pop) +#endif +} + + +void read_file( + char const path[], std::size_t len, std::basic_string &buf) +{ + buf.resize(len); + auto f{my_fopen(path, "rb")}; + auto bytes{ + std::fread(reinterpret_cast(buf.data()), 1, len, f.get())}; + if (bytes == 0) + throw std::runtime_error{"Error reading test file."}; + buf.resize(bytes); +} + + +void write_file(char const path[], std::basic_string_view data) +{ + try + { + auto f{my_fopen(path, "wb")}; + if ( + std::fwrite( + reinterpret_cast(data.data()), 1, std::size(data), + f.get()) < std::size(data)) + std::runtime_error{"File write failed."}; + } + catch (const std::exception &) + { + std::remove(path); + throw; + } +} + + +/// Temporary file. +class TempFile +{ +public: + /// Create (and later clean up) a file at path containing data. + TempFile(char const path[], std::basic_string_view data) : + m_path(path) + { + write_file(path, data); + } + + ~TempFile() { std::remove(m_path.c_str()); } + +private: + std::string m_path; +}; +} // namespace + + +void test_blob_from_file_creates_blob_from_file_contents() +{ + char const temp_file[] = "blob-test-from_file.tmp"; + std::basic_string const data{std::byte{'4'}, std::byte{'2'}}; + + pqxx::connection conn; + pqxx::work tx{conn}; + std::basic_string buf; + + pqxx::oid id; + { + TempFile f{temp_file, data}; + id = pqxx::blob::from_file(tx, temp_file); + } + pqxx::blob::to_buf(tx, id, buf, 10); + PQXX_CHECK_EQUAL(buf, data, "Wrong data from blob::from_file()."); +} + + +void test_blob_from_file_with_oid_writes_blob() +{ + std::basic_string const data{std::byte{'6'}, std::byte{'9'}}; + char const temp_file[] = "blob-test-from_file-oid.tmp"; + std::basic_string buf; + + pqxx::connection conn; + pqxx::work tx{conn}; + + // Guarantee (more or less) that id is not in use. + auto id{pqxx::blob::create(tx)}; + pqxx::blob::remove(tx, id); + + { + TempFile f{temp_file, data}; + pqxx::blob::from_file(tx, temp_file, id); + } + pqxx::blob::to_buf(tx, id, buf, 10); + PQXX_CHECK_EQUAL(buf, data, "Wrong data from blob::from_file()."); +} + + +void test_blob_append_to_buf_appends() +{ + std::basic_string const data{ + std::byte{'b'}, std::byte{'l'}, std::byte{'u'}, std::byte{'b'}}; + + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{pqxx::blob::from_buf(tx, data)}; + + std::basic_string buf; + PQXX_CHECK_EQUAL( + pqxx::blob::append_to_buf(tx, id, 0u, buf, 1u), 1u, + "append_to_buf() returned unexpected value."); + PQXX_CHECK_EQUAL(std::size(buf), 1u, "Appended the wrong number of bytes."); + PQXX_CHECK_EQUAL( + pqxx::blob::append_to_buf(tx, id, 1u, buf, 5u), 3u, + "append_to_buf() returned unexpected value."); + PQXX_CHECK_EQUAL(std::size(buf), 4u, "Appended the wrong number of bytes."); + + PQXX_CHECK_EQUAL( + buf, data, "Reading using append_to_buf gave us wrong data."); +} + + +void test_blob_to_file_writes_file() +{ + std::basic_string const data{ + std::byte{'C'}, std::byte{'+'}, std::byte{'+'}}; + + char const temp_file[] = "blob-test-to_file.tmp"; + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{pqxx::blob::from_buf(tx, data)}; + std::basic_string buf; + + try + { + pqxx::blob::to_file(tx, id, temp_file); + read_file(temp_file, 10u, buf); + std::remove(temp_file); + } + catch (std::exception const &) + { + std::remove(temp_file); + throw; + } + PQXX_CHECK_EQUAL(buf, data, "Got wrong data from to_file()."); +} + + +void test_blob_close_leaves_blob_unusable() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + auto id{ + pqxx::blob::from_buf(tx, std::basic_string{std::byte{1}})}; + auto b{pqxx::blob::open_rw(tx, id)}; + b.close(); + std::basic_string buf; + PQXX_CHECK_THROWS( + b.read(buf, 1), pqxx::usage_error, + "Reading from closed blob did not fail right."); +} + + +void test_blob_accepts_std_filesystem_path() +{ +#if defined(PQXX_HAVE_PATH) && !defined(_WIN32) + // A bug in gcc 8's ~std::filesystem::path() causes a run-time crash. +# if !defined(__GNUC__) || (__GNUC__ > 8) + + char const temp_file[] = "blob-test-filesystem-path.tmp"; + std::basic_string const data{std::byte{'4'}, std::byte{'2'}}; + + pqxx::connection conn; + pqxx::work tx{conn}; + std::basic_string buf; + + TempFile f{temp_file, data}; + std::filesystem::path const path{temp_file}; + auto id{pqxx::blob::from_file(tx, path)}; + pqxx::blob::to_buf(tx, id, buf, 10); + PQXX_CHECK_EQUAL(buf, data, "Wrong data from blob::from_file()."); + +# endif +#endif +} + + +PQXX_REGISTER_TEST(test_blob_is_useless_by_default); +PQXX_REGISTER_TEST(test_blob_create_makes_empty_blob); +PQXX_REGISTER_TEST(test_blob_create_with_oid_requires_oid_be_free); +PQXX_REGISTER_TEST(test_blob_create_with_oid_obeys_oid); +PQXX_REGISTER_TEST(test_blobs_are_transactional); +PQXX_REGISTER_TEST(test_blob_remove_removes_blob); +PQXX_REGISTER_TEST(test_blob_remove_is_not_idempotent); +PQXX_REGISTER_TEST(test_blob_checks_open_mode); +PQXX_REGISTER_TEST(test_blob_supports_move); +PQXX_REGISTER_TEST(test_blob_read_reads_data); +PQXX_REGISTER_TEST(test_blob_reads_vector); +PQXX_REGISTER_TEST(test_blob_read_span); +PQXX_REGISTER_TEST(test_blob_write_appends_at_insertion_point); +PQXX_REGISTER_TEST(test_blob_writes_span); +PQXX_REGISTER_TEST(test_blob_resize_shortens_to_desired_length); +PQXX_REGISTER_TEST(test_blob_resize_extends_to_desired_length); +PQXX_REGISTER_TEST(test_blob_tell_tracks_position); +PQXX_REGISTER_TEST(test_blob_seek_sets_positions); +PQXX_REGISTER_TEST(test_blob_from_buf_interoperates_with_to_buf); +PQXX_REGISTER_TEST(test_blob_append_from_buf_appends); +PQXX_REGISTER_TEST(test_blob_from_file_creates_blob_from_file_contents); +PQXX_REGISTER_TEST(test_blob_from_file_with_oid_writes_blob); +PQXX_REGISTER_TEST(test_blob_append_to_buf_appends); +PQXX_REGISTER_TEST(test_blob_to_file_writes_file); +PQXX_REGISTER_TEST(test_blob_close_leaves_blob_unusable); +PQXX_REGISTER_TEST(test_blob_accepts_std_filesystem_path); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_cancel_query.cxx b/ext/libpqxx-7.7.3/test/unit/test_cancel_query.cxx new file mode 100644 index 000000000..5329d195f --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_cancel_query.cxx @@ -0,0 +1,25 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_cancel_query() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + // Calling cancel_query() while none is in progress has no effect. + conn.cancel_query(); + + // Nothing much is guaranteed about cancel_query, except that it doesn't make + // the process die in flames. + pqxx::pipeline p{tx, "test_cancel_query"}; + p.retain(0); + p.insert("SELECT pg_sleep(1)"); + conn.cancel_query(); +} + + +PQXX_REGISTER_TEST(test_cancel_query); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_column.cxx b/ext/libpqxx-7.7.3/test/unit/test_column.cxx new file mode 100644 index 000000000..9c50faff4 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_column.cxx @@ -0,0 +1,61 @@ +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_table_column() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + + tx.exec0("CREATE TEMP TABLE pqxxfoo (x varchar, y integer, z integer)"); + tx.exec0("INSERT INTO pqxxfoo VALUES ('xx', 1, 2)"); + auto R{tx.exec("SELECT z,y,x FROM pqxxfoo")}; + auto X{tx.exec("SELECT x,y,z,99 FROM pqxxfoo")}; + + pqxx::row::size_type x{R.table_column(2)}, y{R.table_column(1)}, + z{R.table_column(static_cast(0))}; + + PQXX_CHECK_EQUAL(x, 0, "Wrong column number."); + PQXX_CHECK_EQUAL(y, 1, "Wrong column number."); + PQXX_CHECK_EQUAL(z, 2, "Wrong column number."); + + x = R.table_column("x"); + y = R.table_column("y"); + z = R.table_column("z"); + + PQXX_CHECK_EQUAL(x, 0, "Wrong number for named column."); + PQXX_CHECK_EQUAL(y, 1, "Wrong number for named column."); + PQXX_CHECK_EQUAL(z, 2, "Wrong number for named column."); + + pqxx::row::size_type xx{X[0].table_column(static_cast(0))}, + yx{X[0].table_column(pqxx::row::size_type(1))}, zx{X[0].table_column("z")}; + + PQXX_CHECK_EQUAL(xx, 0, "Bad result from table_column(int)."); + PQXX_CHECK_EQUAL(yx, 1, "Bad result from table_column(size_type)."); + PQXX_CHECK_EQUAL(zx, 2, "Bad result from table_column(string)."); + + for (pqxx::row::size_type i{0}; i < std::size(R[0]); ++i) + PQXX_CHECK_EQUAL( + R[0][i].table_column(), R.table_column(i), + "Bad result from column_table()."); + + int col; + PQXX_CHECK_THROWS_EXCEPTION( + col = R.table_column(3), "table_column() with invalid index didn't fail."); + pqxx::ignore_unused(col); + + PQXX_CHECK_THROWS_EXCEPTION( + col = R.table_column("nonexistent"), + "table_column() with invalid column name didn't fail."); + pqxx::ignore_unused(col); + + PQXX_CHECK_THROWS_EXCEPTION( + col = X.table_column(3), "table_column() on non-table didn't fail."); + pqxx::ignore_unused(col); +} +} // namespace + + +PQXX_REGISTER_TEST(test_table_column); diff --git a/ext/libpqxx-7.7.3/test/unit/test_composite.cxx b/ext/libpqxx-7.7.3/test/unit/test_composite.cxx new file mode 100644 index 000000000..dcd65360c --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_composite.cxx @@ -0,0 +1,98 @@ +#include "../test_helpers.hxx" + +#include "pqxx/composite" +#include "pqxx/transaction" + +namespace +{ +void test_composite() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + tx.exec0("CREATE TYPE pqxxfoo AS (a integer, b text)"); + auto const r{tx.exec1("SELECT '(5,hello)'::pqxxfoo")}; + + int a; + std::string b; + pqxx::parse_composite(r[0].view(), a, b); + + PQXX_CHECK_EQUAL(a, 5, "Integer composite field came back wrong."); + PQXX_CHECK_EQUAL(b, "hello", "String composite field came back wrong."); +} + + +void test_composite_escapes() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::row r; + tx.exec0("CREATE TYPE pqxxsingle AS (x text)"); + std::string s; + + r = tx.exec1(R"--(SELECT '("a""b")'::pqxxsingle)--"); + pqxx::parse_composite(r[0].view(), s); + PQXX_CHECK_EQUAL( + s, "a\"b", "Double-double-quotes escaping did not parse correctly."); + + r = tx.exec1(R"--(SELECT '("a\"b")'::pqxxsingle)--"); + pqxx::parse_composite(r[0].view(), s); + PQXX_CHECK_EQUAL(s, "a\"b", "Backslash escaping did not parse correctly."); +} + + +void test_composite_handles_nulls() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::row r; + + tx.exec0("CREATE TYPE pqxxnull AS (a integer)"); + int nonnull; + r = tx.exec1("SELECT '()'::pqxxnull"); + PQXX_CHECK_THROWS( + pqxx::parse_composite(r[0].view(), nonnull), pqxx::conversion_error, + "No conversion error when reading a null into a nulless variable."); + std::optional nullable{5}; + pqxx::parse_composite(r[0].view(), nullable); + PQXX_CHECK( + not nullable.has_value(), "Null integer came out as having a value."); + + tx.exec0("CREATE TYPE pqxxnulls AS (a integer, b integer)"); + std::optional a{2}, b{4}; + r = tx.exec1("SELECT '(,)'::pqxxnulls"); + pqxx::parse_composite(r[0].view(), a, b); + PQXX_CHECK(not a.has_value(), "Null first integer stored as value."); + PQXX_CHECK(not b.has_value(), "Null second integer stored as value."); +} + + +void test_composite_renders_to_string() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + char buf[1000]; + + pqxx::composite_into_buf( + std::begin(buf), std::end(buf), 355, "foo", "b\na\\r"); + PQXX_CHECK_EQUAL( + std::string{buf}, "(355,\"foo\",\"b\na\\\\r\")", + "Composite was not rendered as expected."); + + tx.exec0("CREATE TYPE pqxxcomp AS (a integer, b text, c text)"); + auto const r{tx.exec1("SELECT '" + std::string{buf} + "'::pqxxcomp")}; + + int a; + std::string b, c; + bool const nonnull{r[0].composite_to(a, b, c)}; + PQXX_CHECK(nonnull, "Mistaken nullness."); + PQXX_CHECK_EQUAL(a, 355, "Int came back wrong."); + PQXX_CHECK_EQUAL(b, "foo", "Simple string came back wrong."); + PQXX_CHECK_EQUAL(c, "b\na\\r", "Escaping went wrong."); +} + + +PQXX_REGISTER_TEST(test_composite); +PQXX_REGISTER_TEST(test_composite_escapes); +PQXX_REGISTER_TEST(test_composite_handles_nulls); +PQXX_REGISTER_TEST(test_composite_renders_to_string); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_connection.cxx b/ext/libpqxx-7.7.3/test/unit/test_connection.cxx new file mode 100644 index 000000000..bbb780854 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_connection.cxx @@ -0,0 +1,212 @@ +#include + +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_connection_string_constructor() +{ + pqxx::connection c1{""}; + pqxx::connection c2{std::string{}}; +} + + +void test_move_constructor() +{ + pqxx::connection c1; + PQXX_CHECK(c1.is_open(), "New connection is not open."); + + pqxx::connection c2{std::move(c1)}; + + PQXX_CHECK(not c1.is_open(), "Moving did not close original connection."); + PQXX_CHECK(c2.is_open(), "Moved constructor is not open."); + + pqxx::work tx{c2}; + PQXX_CHECK_EQUAL(tx.query_value("SELECT 5"), 5, "Weird result!"); + + PQXX_CHECK_THROWS( + pqxx::connection c3{std::move(c2)}, pqxx::usage_error, + "Moving a connection with a transaction open should be an error."); +} + + +void test_move_assign() +{ + pqxx::connection c1; + pqxx::connection c2; + + c2.close(); + + c2 = std::move(c1); + + PQXX_CHECK(not c1.is_open(), "Connection still open after being moved out."); + PQXX_CHECK(c2.is_open(), "Moved constructor is not open."); + + { + pqxx::work tx1{c2}; + PQXX_CHECK_EQUAL(tx1.query_value("SELECT 8"), 8, "What!?"); + + pqxx::connection c3; + PQXX_CHECK_THROWS( + c3 = std::move(c2), pqxx::usage_error, + "Moving a connection with a transaction open should be an error."); + + PQXX_CHECK_THROWS( + c2 = std::move(c3), pqxx::usage_error, + "Moving a connection onto one with a transaction open should be " + "an error."); + } + + // After failed move attempts, the connection is still usable. + pqxx::work tx2{c2}; + PQXX_CHECK_EQUAL(tx2.query_value("SELECT 6"), 6, "Huh!?"); +} + + +void test_encrypt_password() +{ + pqxx::connection c; + auto pw{c.encrypt_password("user", "password")}; + PQXX_CHECK(not std::empty(pw), "Encrypted password was empty."); + PQXX_CHECK_EQUAL( + std::strlen(pw.c_str()), std::size(pw), + "Encrypted password contains a null byte."); +} + + +void test_connection_string() +{ + pqxx::connection c; + std::string const connstr{c.connection_string()}; + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4996) +#endif + if (std::getenv("PGUSER") == nullptr) +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + { + PQXX_CHECK( + connstr.find("user=" + std::string{c.username()}) != std::string::npos, + "Connection string did not specify user name: " + connstr); + } + else + { + PQXX_CHECK( + connstr.find("user=" + std::string{c.username()}) == std::string::npos, + "Connection string specified user name, even when using default: " + + connstr); + } +} + + +#if defined(PQXX_HAVE_CONCEPTS) +template std::size_t length(STR const &str) +{ + return std::size(str); +} + + +std::size_t length(char const str[]) +{ + return std::strlen(str); +} +#endif // PQXX_HAVE_CONCEPTS + + +template void test_params_type() +{ +#if defined(PQXX_HAVE_CONCEPTS) + using item_t = std::remove_reference_t< + decltype(*std::declval>())>; + using key_t = decltype(std::get<0>(std::declval())); + using value_t = decltype(std::get<1>(std::declval())); + + // Set some parameters that are relatively safe to change arbitrarily. + MAP const params{{ + {key_t{"application_name"}, value_t{"pqxx-test"}}, + {key_t{"connect_timeout"}, value_t{"96"}}, + {key_t{"keepalives_idle"}, value_t{"771"}}, + }}; + + // Can we create a connection from these parameters? + pqxx::connection c{params}; + + // Check that the parameters came through in the connection string. + // We don't know the exact format, but the parameters have to be in there. + auto const min_size{std::accumulate( + std::cbegin(params), std::cend(params), std::size(params) - 1, + [](auto count, auto item) { + return count + length(std::get<0>(item)) + 1 + length(std::get<1>(item)); + })}; + + auto const connstr{c.connection_string()}; + PQXX_CHECK_GREATER_EQUAL( + std::size(connstr), min_size, + "Connection string can't possibly contain the options we gave."); + for (auto const &[key, value] : params) + { + PQXX_CHECK_NOT_EQUAL( + connstr.find(key), std::string::npos, + "Could not find param name '" + std::string{key} + + "' in connection string: " + connstr); + PQXX_CHECK_NOT_EQUAL( + connstr.find(value), std::string::npos, + "Could not find value for '" + std::string{value} + + "' in connection string: " + connstr); + } +#endif // PQXX_HAVE_CONCEPTS +} + + +void test_connection_params() +{ + // Connecting in this way supports a wide variety of formats for the + // parameters. + test_params_type>(); + test_params_type>(); + test_params_type>(); + test_params_type>(); + test_params_type>(); + test_params_type>>(); + test_params_type>>(); + test_params_type>>(); + test_params_type>>(); +} + + +void test_raw_connection() +{ + pqxx::connection conn1; + PQXX_CHECK(conn1.is_open(), "Fresh connection is not open!"); + pqxx::work tx1{conn1}; + PQXX_CHECK_EQUAL( + tx1.query_value("SELECT 8"), 8, "Something weird happened."); + pqxx::internal::pq::PGconn *raw{std::move(conn1).release_raw_connection()}; + PQXX_CHECK(raw != nullptr, "Raw connection is null."); + PQXX_CHECK( + not conn1.is_open(), + "Releasing raw connection did not close pqxx::connection."); + + pqxx::connection conn2{pqxx::connection::seize_raw_connection(raw)}; + PQXX_CHECK( + conn2.is_open(), "Can't produce open connection from raw connection."); + pqxx::work tx2{conn2}; + PQXX_CHECK_EQUAL( + tx2.query_value("SELECT 9"), 9, + "Raw connection did not produce a working new connection."); +} + + +PQXX_REGISTER_TEST(test_connection_string_constructor); +PQXX_REGISTER_TEST(test_move_constructor); +PQXX_REGISTER_TEST(test_move_assign); +PQXX_REGISTER_TEST(test_encrypt_password); +PQXX_REGISTER_TEST(test_connection_string); +PQXX_REGISTER_TEST(test_connection_params); +PQXX_REGISTER_TEST(test_raw_connection); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_cursor.cxx b/ext/libpqxx-7.7.3/test/unit/test_cursor.cxx new file mode 100644 index 000000000..8761f6946 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_cursor.cxx @@ -0,0 +1,50 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_stateless_cursor_provides_random_access(pqxx::connection &conn) +{ + pqxx::work tx{conn}; + pqxx::stateless_cursor< + pqxx::cursor_base::read_only, pqxx::cursor_base::owned> + c{tx, "SELECT * FROM generate_series(0, 3)", "count", false}; + + auto r{c.retrieve(1, 2)}; + PQXX_CHECK_EQUAL(std::size(r), 1, "Wrong number of rows from retrieve()."); + PQXX_CHECK_EQUAL(r[0][0].as(), 1, "Cursor retrieved wrong data."); + + r = c.retrieve(3, 10); + PQXX_CHECK_EQUAL(std::size(r), 1, "Expected 1 row retrieving past end."); + PQXX_CHECK_EQUAL(r[0][0].as(), 3, "Wrong data retrieved at end."); + + r = c.retrieve(0, 1); + PQXX_CHECK_EQUAL(std::size(r), 1, "Wrong number of rows back at beginning."); + PQXX_CHECK_EQUAL(r[0][0].as(), 0, "Wrong data back at beginning."); +} + + +void test_stateless_cursor_ignores_trailing_semicolon(pqxx::connection &conn) +{ + pqxx::work tx{conn}; + pqxx::stateless_cursor< + pqxx::cursor_base::read_only, pqxx::cursor_base::owned> + c{tx, "SELECT * FROM generate_series(0, 3) ;; ; \n \t ", "count", false}; + + auto r{c.retrieve(1, 2)}; + PQXX_CHECK_EQUAL(std::size(r), 1, "Trailing semicolon confused retrieve()."); +} + + +void test_cursor() +{ + pqxx::connection conn; + test_stateless_cursor_provides_random_access(conn); + test_stateless_cursor_ignores_trailing_semicolon(conn); +} + + +PQXX_REGISTER_TEST(test_cursor); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_encodings.cxx b/ext/libpqxx-7.7.3/test/unit/test_encodings.cxx new file mode 100644 index 000000000..4374671c6 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_encodings.cxx @@ -0,0 +1,114 @@ +#include "../test_helpers.hxx" + +#include "pqxx/internal/encodings.hxx" + + +namespace +{ +void test_scan_ascii() +{ + auto const scan{pqxx::internal::get_glyph_scanner( + pqxx::internal::encoding_group::MONOBYTE)}; + std::string const text{"hello"}; + + PQXX_CHECK_EQUAL( + scan(text.c_str(), std::size(text), 0), 1ul, + "Monobyte scanner acting up."); + PQXX_CHECK_EQUAL( + scan(text.c_str(), std::size(text), 1), 2ul, + "Monobyte scanner is inconsistent."); +} + + +void test_scan_utf8() +{ + auto const scan{ + pqxx::internal::get_glyph_scanner(pqxx::internal::encoding_group::UTF8)}; + + // Thai: "Khrab". + std::string const text{"\xe0\xb8\x95\xe0\xb8\xa3\xe0\xb8\xb1\xe0\xb8\x9a"}; + PQXX_CHECK_EQUAL( + scan(text.c_str(), std::size(text), 0), 3ul, + "UTF-8 scanner mis-scanned Thai khor khwai."); + PQXX_CHECK_EQUAL( + scan(text.c_str(), std::size(text), 3), 6ul, + "UTF-8 scanner mis-scanned Thai ror reua."); +} + + +void test_for_glyphs_empty() +{ + bool iterated{false}; + pqxx::internal::for_glyphs( + pqxx::internal::encoding_group::MONOBYTE, + [&iterated](char const *, char const *) { iterated = true; }, "", 0); + PQXX_CHECK(!iterated, "Empty string went through an iteration."); +} + + +void test_for_glyphs_ascii() +{ + std::string const text{"hi"}; + std::vector points; + + pqxx::internal::for_glyphs( + pqxx::internal::encoding_group::UTF8, + [&points](char const *gbegin, char const *gend) { + points.push_back(gend - gbegin); + }, + text.c_str(), std::size(text)); + + PQXX_CHECK_EQUAL(std::size(points), 2u, "Wrong number of ASCII iterations."); + PQXX_CHECK_EQUAL(points[0], 1u, "ASCII iteration started off wrong."); + PQXX_CHECK_EQUAL(points[1], 1u, "ASCII iteration was inconsistent."); +} + + +void test_for_glyphs_utf8() +{ + // Greek: alpha omega. + std::string const text{"\xce\x91\xce\xa9"}; + std::vector points; + + pqxx::internal::for_glyphs( + pqxx::internal::encoding_group::UTF8, + [&points](char const *gbegin, char const *gend) { + points.push_back(gend - gbegin); + }, + text.c_str(), std::size(text)); + + PQXX_CHECK_EQUAL(std::size(points), 2u, "Wrong number of UTF-8 iterations."); + PQXX_CHECK_EQUAL(points[0], 2u, "UTF-8 iteration started off wrong."); + PQXX_CHECK_EQUAL(points[1], 2u, "ASCII iteration was inconsistent."); + + // Greek lambda, ASCII plus sign, Old Persian Gu. + std::string const mix{"\xce\xbb+\xf0\x90\x8e\xa6"}; + points.clear(); + + pqxx::internal::for_glyphs( + pqxx::internal::encoding_group::UTF8, + [&points](char const *gbegin, char const *gend) { + points.push_back(gend - gbegin); + }, + mix.c_str(), std::size(mix)); + + PQXX_CHECK_EQUAL(std::size(points), 3u, "Mixed UTF-8 iteration is broken."); + PQXX_CHECK_EQUAL(points[0], 2u, "Mixed UTF-8 iteration started off wrong."); + PQXX_CHECK_EQUAL(points[1], 1u, "Mixed UTF-8 iteration got ASCII wrong."); + PQXX_CHECK_EQUAL( + points[2], 4u, "Mixed UTF-8 iteration got long char wrong."); +} + + +void test_encodings() +{ + test_scan_ascii(); + test_scan_utf8(); + test_for_glyphs_empty(); + test_for_glyphs_ascii(); + test_for_glyphs_utf8(); +} + + +PQXX_REGISTER_TEST(test_encodings); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_error_verbosity.cxx b/ext/libpqxx-7.7.3/test/unit/test_error_verbosity.cxx new file mode 100644 index 000000000..67dc88763 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_error_verbosity.cxx @@ -0,0 +1,37 @@ +#include + +#include "../test_helpers.hxx" + +extern "C" +{ +#include +} + +namespace +{ +void test_error_verbosity() +{ + PQXX_CHECK_EQUAL( + static_cast(pqxx::error_verbosity::terse), + static_cast(PQERRORS_TERSE), + "error_verbosity enum should match PGVerbosity."); + PQXX_CHECK_EQUAL( + static_cast(pqxx::error_verbosity::normal), + static_cast(PQERRORS_DEFAULT), + "error_verbosity enum should match PGVerbosity."); + PQXX_CHECK_EQUAL( + static_cast(pqxx::error_verbosity::verbose), + static_cast(PQERRORS_VERBOSE), + "error_verbosity enum should match PGVerbosity."); + + pqxx::connection conn; + pqxx::work tx{conn}; + conn.set_verbosity(pqxx::error_verbosity::terse); + tx.exec1("SELECT 1"); + conn.set_verbosity(pqxx::error_verbosity::verbose); + tx.exec1("SELECT 2"); +} + + +PQXX_REGISTER_TEST(test_error_verbosity); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_errorhandler.cxx b/ext/libpqxx-7.7.3/test/unit/test_errorhandler.cxx new file mode 100644 index 000000000..c2cd4a797 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_errorhandler.cxx @@ -0,0 +1,223 @@ +#include + +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +class TestErrorHandler final : public pqxx::errorhandler +{ +public: + TestErrorHandler( + pqxx::connection &c, std::vector &activated_handlers, + bool retval = true) : + pqxx::errorhandler(c), + return_value(retval), + message(), + handler_list(activated_handlers) + {} + + bool operator()(char const msg[]) noexcept override + { + message = std::string{msg}; + handler_list.push_back(this); + return return_value; + } + + bool return_value; + std::string message; + std::vector &handler_list; +}; +} // namespace + + +namespace pqxx +{ +template<> struct nullness +{ + // clang warns about these being unused. And clang 6 won't accept a + // [[maybe_unused]] attribute on them either! + + // static inline constexpr bool has_null{true}; + // static inline constexpr bool always_null{false}; + + static constexpr bool is_null(TestErrorHandler *e) noexcept + { + return e == nullptr; + } + static constexpr TestErrorHandler *null() noexcept { return nullptr; } +}; + + +template<> struct string_traits +{ + static constexpr std::size_t size_buffer(TestErrorHandler *const &) noexcept + { + return 100; + } + + static char *into_buf(char *begin, char *end, TestErrorHandler *const &value) + { + std::string text{"TestErrorHandler at " + pqxx::to_string(value)}; + if (pqxx::internal::cmp_greater_equal(std::size(text), end - begin)) + throw conversion_overrun{"Not enough buffer for TestErrorHandler."}; + std::memcpy(begin, text.c_str(), std::size(text) + 1); + return begin + std::size(text) + 1; + } +}; +} // namespace pqxx + + +namespace +{ +void test_process_notice_calls_errorhandler(pqxx::connection &c) +{ + std::vector dummy; + TestErrorHandler handler(c, dummy); + c.process_notice("Error!\n"); + PQXX_CHECK_EQUAL(handler.message, "Error!\n", "Error not handled."); +} + + +void test_error_handlers_get_called_newest_to_oldest(pqxx::connection &c) +{ + std::vector handlers; + TestErrorHandler h1(c, handlers); + TestErrorHandler h2(c, handlers); + TestErrorHandler h3(c, handlers); + c.process_notice("Warning.\n"); + PQXX_CHECK_EQUAL(h3.message, "Warning.\n", "Message not handled."); + PQXX_CHECK_EQUAL(h2.message, "Warning.\n", "Broken handling chain."); + PQXX_CHECK_EQUAL(h1.message, "Warning.\n", "Insane handling chain."); + PQXX_CHECK_EQUAL(std::size(handlers), 3u, "Wrong number of handler calls."); + PQXX_CHECK_EQUAL(&h3, handlers[0], "Unexpected handling order."); + PQXX_CHECK_EQUAL(&h2, handlers[1], "Insane handling order."); + PQXX_CHECK_EQUAL(&h1, handlers[2], "Impossible handling order."); +} + +void test_returning_false_stops_error_handling(pqxx::connection &c) +{ + std::vector handlers; + TestErrorHandler starved(c, handlers); + TestErrorHandler blocker(c, handlers, false); + c.process_notice("Error output.\n"); + PQXX_CHECK_EQUAL(std::size(handlers), 1u, "Handling chain was not stopped."); + PQXX_CHECK_EQUAL(handlers[0], &blocker, "Wrong handler got message."); + PQXX_CHECK_EQUAL(blocker.message, "Error output.\n", "Didn't get message."); + PQXX_CHECK_EQUAL(starved.message, "", "Message received; it shouldn't be."); +} + +void test_destroyed_error_handlers_are_not_called(pqxx::connection &c) +{ + std::vector handlers; + { + TestErrorHandler doomed(c, handlers); + } + c.process_notice("Unheard output."); + PQXX_CHECK( + std::empty(handlers), "Message was received on dead errorhandler."); +} + +void test_destroying_connection_unregisters_handlers() +{ + TestErrorHandler *survivor; + std::vector handlers; + { + pqxx::connection c; + survivor = new TestErrorHandler(c, handlers); + } + // Make some pointless use of survivor just to prove that this doesn't crash. + (*survivor)("Hi"); + PQXX_CHECK_EQUAL( + std::size(handlers), 1u, "Ghost of dead ex-connection haunts handler."); + delete survivor; +} + + +class MinimalErrorHandler final : public pqxx::errorhandler +{ +public: + explicit MinimalErrorHandler(pqxx::connection &c) : pqxx::errorhandler(c) {} + bool operator()(char const[]) noexcept override { return true; } +}; + + +void test_get_errorhandlers(pqxx::connection &c) +{ + std::unique_ptr eh3; + auto const handlers_before{c.get_errorhandlers()}; + std::size_t const base_handlers{std::size(handlers_before)}; + + { + MinimalErrorHandler eh1(c); + auto const handlers_with_eh1{c.get_errorhandlers()}; + PQXX_CHECK_EQUAL( + std::size(handlers_with_eh1), base_handlers + 1, + "Registering a handler didn't create exactly one handler."); + PQXX_CHECK_EQUAL( + std::size_t(*std::rbegin(handlers_with_eh1)), std::size_t(&eh1), + "Wrong handler or wrong order."); + + { + MinimalErrorHandler eh2(c); + auto const handlers_with_eh2{c.get_errorhandlers()}; + PQXX_CHECK_EQUAL( + std::size(handlers_with_eh2), base_handlers + 2, + "Adding second handler didn't work."); + PQXX_CHECK_EQUAL( + std::size_t(*(std::rbegin(handlers_with_eh2) + 1)), std::size_t(&eh1), + "Second handler upset order."); + PQXX_CHECK_EQUAL( + std::size_t(*std::rbegin(handlers_with_eh2)), std::size_t(&eh2), + "Second handler isn't right."); + } + auto const handlers_without_eh2{c.get_errorhandlers()}; + PQXX_CHECK_EQUAL( + std::size(handlers_without_eh2), base_handlers + 1, + "Handler destruction produced wrong-sized handlers list."); + PQXX_CHECK_EQUAL( + std::size_t(*std::rbegin(handlers_without_eh2)), std::size_t(&eh1), + "Destroyed wrong handler."); + + eh3 = std::make_unique(c); + auto const handlers_with_eh3{c.get_errorhandlers()}; + PQXX_CHECK_EQUAL( + std::size(handlers_with_eh3), base_handlers + 2, + "Remove-and-add breaks."); + PQXX_CHECK_EQUAL( + std::size_t(*std::rbegin(handlers_with_eh3)), std::size_t(eh3.get()), + "Added wrong third handler."); + } + auto const handlers_without_eh1{c.get_errorhandlers()}; + PQXX_CHECK_EQUAL( + std::size(handlers_without_eh1), base_handlers + 1, + "Destroying oldest handler didn't work as expected."); + PQXX_CHECK_EQUAL( + std::size_t(*std::rbegin(handlers_without_eh1)), std::size_t(eh3.get()), + "Destroyed wrong handler."); + + eh3.reset(); + + auto const handlers_without_all{c.get_errorhandlers()}; + PQXX_CHECK_EQUAL( + std::size(handlers_without_all), base_handlers, + "Destroying all custom handlers didn't work as expected."); +} + + +void test_errorhandler() +{ + pqxx::connection conn; + test_process_notice_calls_errorhandler(conn); + test_error_handlers_get_called_newest_to_oldest(conn); + test_returning_false_stops_error_handling(conn); + test_destroyed_error_handlers_are_not_called(conn); + test_destroying_connection_unregisters_handlers(); + test_get_errorhandlers(conn); +} + + +PQXX_REGISTER_TEST(test_errorhandler); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_escape.cxx b/ext/libpqxx-7.7.3/test/unit/test_escape.cxx new file mode 100644 index 000000000..baff4d62c --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_escape.cxx @@ -0,0 +1,228 @@ +#include + +#include + +#include "../test_helpers.hxx" + +namespace +{ +using namespace std::literals; + + +void compare_esc( + pqxx::connection &c, pqxx::transaction_base &t, char const text[]) +{ + std::size_t const len{std::size(std::string{text})}; + PQXX_CHECK_EQUAL( + c.esc(std::string_view{text, len}), t.esc(std::string_view{text, len}), + "Connection & transaction escape differently."); + + PQXX_CHECK_EQUAL( + t.esc(std::string_view{text, len}), t.esc(text), + "Length argument to esc() changes result."); + + PQXX_CHECK_EQUAL( + t.esc(std::string{text}), t.esc(text), + "esc(std::string()) differs from esc(char const[])."); + + PQXX_CHECK_EQUAL( + text, + t.query_value( + "SELECT '" + t.esc(std::string_view{text, len}) + "'"), + "esc() is not idempotent."); + + PQXX_CHECK_EQUAL( + t.esc(std::string_view{text, len}), t.esc(text), + "Oversized buffer affects esc()."); +} + + +void test_esc(pqxx::connection &c, pqxx::transaction_base &t) +{ + PQXX_CHECK_EQUAL( + t.esc(std::string_view{"", 0}), "", + "Empty string doesn't escape properly."); + PQXX_CHECK_EQUAL( + t.esc(std::string_view{"'", 1}), "''", + "Single quote escaped incorrectly."); + PQXX_CHECK_EQUAL( + t.esc(std::string_view{"hello"}), "hello", "Trivial escape went wrong."); + char const *const escstrings[]{"x", " ", "", nullptr}; + for (std::size_t i{0}; escstrings[i] != nullptr; ++i) + compare_esc(c, t, escstrings[i]); +} + + +void test_quote(pqxx::connection &c, pqxx::transaction_base &t) +{ + PQXX_CHECK_EQUAL(t.quote("x"), "'x'", "Basic quote() fails."); + PQXX_CHECK_EQUAL( + t.quote(1), "'1'", "quote() not dealing with int properly."); + PQXX_CHECK_EQUAL(t.quote(0), "'0'", "Quoting zero is a problem."); + char const *const null_ptr{nullptr}; + PQXX_CHECK_EQUAL(t.quote(null_ptr), "NULL", "Not quoting NULL correctly."); + PQXX_CHECK_EQUAL( + t.quote(std::string{"'"}), "''''", "Escaping quotes goes wrong."); + + PQXX_CHECK_EQUAL( + t.quote("x"), c.quote("x"), + "Connection and transaction quote differently."); + + char const *test_strings[]{"", "x", "\\", "\\\\", "'", + "''", "\\'", "\t", "\n", nullptr}; + + for (std::size_t i{0}; test_strings[i] != nullptr; ++i) + { + auto r{t.query_value("SELECT " + t.quote(test_strings[i]))}; + PQXX_CHECK_EQUAL( + r, test_strings[i], "Selecting quoted string does not come back equal."); + } +} + + +void test_quote_name(pqxx::transaction_base &t) +{ + PQXX_CHECK_EQUAL( + "\"A b\"", t.quote_name("A b"), "Escaped identifier is not as expected."); + PQXX_CHECK_EQUAL( + std::string{"A b"}, + t.exec("SELECT 1 AS " + t.quote_name("A b")).column_name(0), + "Escaped identifier does not work in SQL."); +} + + +void test_esc_raw_unesc_raw(pqxx::transaction_base &t) +{ + constexpr char binary[]{"1\0023\\4x5"}; + std::basic_string const data( + reinterpret_cast(binary), std::size(binary)); + std::string const escaped{t.esc_raw( + std::basic_string_view{std::data(data), std::size(binary)})}; + + for (auto const i : escaped) + { + PQXX_CHECK_GREATER( + static_cast(static_cast(i)), 7u, + "Non-ASCII character in escaped data: " + escaped); + PQXX_CHECK_LESS( + static_cast(static_cast(i)), 127u, + "Non-ASCII character in escaped data: " + escaped); + } + + for (auto const i : escaped) + PQXX_CHECK( + isprint(i), "Unprintable character in escaped data: " + escaped); + + PQXX_CHECK_EQUAL( + escaped, "\\x3102335c34783500", "Binary data escaped wrong."); + PQXX_CHECK_EQUAL( + std::size(t.unesc_bin(escaped)), std::size(data), + "Wrong size after unescaping."); + auto unescaped{t.unesc_bin(escaped)}; + PQXX_CHECK_EQUAL( + std::size(unescaped), std::size(data), + "Unescaping did not restore original size."); + for (std::size_t i{0}; i < std::size(unescaped); ++i) + PQXX_CHECK_EQUAL( + int(unescaped[i]), int(data[i]), + "Unescaping binary data did not restore byte #" + pqxx::to_string(i) + + "."); +} + + +void test_esc_like(pqxx::transaction_base &tx) +{ + PQXX_CHECK_EQUAL(tx.esc_like(""), "", "esc_like breaks on empty string."); + PQXX_CHECK_EQUAL(tx.esc_like("abc"), "abc", "esc_like is broken."); + PQXX_CHECK_EQUAL(tx.esc_like("_"), "\\_", "esc_like fails on underscore."); + PQXX_CHECK_EQUAL(tx.esc_like("%"), "\\%", "esc_like fails on %."); + PQXX_CHECK_EQUAL( + tx.esc_like("a%b_c"), "a\\%b\\_c", "esc_like breaks on mix."); + PQXX_CHECK_EQUAL( + tx.esc_like("_", '+'), "+_", "esc_like ignores escape character."); +} + + +void test_escaping() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + test_esc(conn, tx); + test_quote(conn, tx); + test_quote_name(tx); + test_esc_raw_unesc_raw(tx); + test_esc_like(tx); +} + + +void test_esc_escapes_into_buffer() +{ +#if defined(PQXX_HAVE_CONCEPTS) + pqxx::connection conn; + pqxx::work tx{conn}; + + std::string buffer; + buffer.resize(20); + + auto const text{"Ain't"sv}; + auto escaped_text{tx.esc(text, buffer)}; + PQXX_CHECK_EQUAL(escaped_text, "Ain''t", "Escaping into buffer went wrong."); + + std::basic_string const data{std::byte{0x22}, std::byte{0x43}}; + auto escaped_data(tx.esc(data, buffer)); + PQXX_CHECK_EQUAL(escaped_data, "\\x2243", "Binary data escaped wrong."); +#endif +} + + +void test_esc_accepts_various_types() +{ +#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) + pqxx::connection conn; + pqxx::work tx{conn}; + + std::string buffer; + buffer.resize(20); + + std::string const text{"it's"}; + auto escaped_text{tx.esc(text, buffer)}; + PQXX_CHECK_EQUAL(escaped_text, "it''s", "Escaping into buffer went wrong."); + + std::vector const data{std::byte{0x23}, std::byte{0x44}}; + auto escaped_data(tx.esc(data, buffer)); + PQXX_CHECK_EQUAL(escaped_data, "\\x2344", "Binary data escaped wrong."); +#endif +} + + +void test_binary_esc_checks_buffer_length() +{ +#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) + pqxx::connection conn; + pqxx::work tx{conn}; + + std::string buf; + std::basic_string bin{ + std::byte{'b'}, std::byte{'o'}, std::byte{'o'}}; + + buf.resize(2 * std::size(bin) + 3); + pqxx::ignore_unused(tx.esc(bin, buf)); + PQXX_CHECK_EQUAL(int{buf[0]}, int{'\\'}, "Unexpected binary escape format."); + PQXX_CHECK_NOT_EQUAL( + int(buf[std::size(buf) - 2]), int('\0'), "Escaped binary ends too soon."); + PQXX_CHECK_EQUAL( + int(buf[std::size(buf) - 1]), int('\0'), "Terminating zero is missing."); + + buf.resize(2 * std::size(bin) + 2); + PQXX_CHECK_THROWS( + pqxx::ignore_unused(tx.esc(bin, buf)), pqxx::range_error, + "Didn't get expected exception from escape overrun."); +#endif +} + + +PQXX_REGISTER_TEST(test_escaping); +PQXX_REGISTER_TEST(test_esc_escapes_into_buffer); +PQXX_REGISTER_TEST(test_esc_accepts_various_types); +PQXX_REGISTER_TEST(test_binary_esc_checks_buffer_length); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_exceptions.cxx b/ext/libpqxx-7.7.3/test/unit/test_exceptions.cxx new file mode 100644 index 000000000..4b84b7fba --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_exceptions.cxx @@ -0,0 +1,45 @@ +#include +#include + +#include "../test_helpers.hxx" + + +namespace +{ +void test_exceptions() +{ + std::string const broken_query{"SELECT HORRIBLE ERROR"}, + err{"Error message"}; + + try + { + throw pqxx::sql_error{err, broken_query}; + } + catch (std::exception const &e) + { + PQXX_CHECK_EQUAL(e.what(), err, "Exception contains wrong message."); + auto downcast{dynamic_cast(&e)}; + PQXX_CHECK( + downcast != nullptr, "exception-to-sql_error downcast is broken."); + PQXX_CHECK_EQUAL( + downcast->query(), broken_query, + "Getting query from pqxx exception is broken."); + } + + pqxx::connection conn; + pqxx::work tx{conn}; + try + { + tx.exec("INVALID QUERY HERE"); + } + catch (pqxx::syntax_error const &e) + { + // SQL syntax error has sqlstate error 42601. + PQXX_CHECK_EQUAL( + e.sqlstate(), "42601", "Unexpected sqlstate on syntax error."); + } +} + + +PQXX_REGISTER_TEST(test_exceptions); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_field.cxx b/ext/libpqxx-7.7.3/test/unit/test_field.cxx new file mode 100644 index 000000000..013ea1a82 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_field.cxx @@ -0,0 +1,57 @@ +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_field() +{ + pqxx::connection c; + pqxx::work tx{c}; + auto const r1{tx.exec1("SELECT 9")}; + auto const &f1{r1[0]}; + + PQXX_CHECK_EQUAL(f1.as(), "9", "as() is broken."); + PQXX_CHECK_EQUAL( + f1.as("z"), "9", "as(string) is broken."); + + PQXX_CHECK_EQUAL(f1.as(), 9, "as() is broken."); + PQXX_CHECK_EQUAL(f1.as(10), 9, "as(int) is broken."); + + std::string s; + PQXX_CHECK(f1.to(s), "to(string) failed."); + PQXX_CHECK_EQUAL(s, "9", "to(string) is broken."); + s = "x"; + PQXX_CHECK(f1.to(s, std::string{"7"}), "to(string, string) failed."); + PQXX_CHECK_EQUAL(s, "9", "to(string, string) is broken."); + + int i{}; + PQXX_CHECK(f1.to(i), "to(int) failed."); + PQXX_CHECK_EQUAL(i, 9, "to(int) is broken."); + i = 8; + PQXX_CHECK(f1.to(i, 12), "to(int, int) failed."); + PQXX_CHECK_EQUAL(i, 9, "to(int, int) is broken."); + + auto const r2{tx.exec1("SELECT NULL")}; + auto const f2{r2[0]}; + i = 100; + PQXX_CHECK_THROWS( + f2.as(), pqxx::conversion_error, "Null conversion failed to throw."); + PQXX_CHECK_EQUAL(i, 100, "Null conversion touched its output."); + + PQXX_CHECK_EQUAL(f2.as(66), 66, "as default is broken."); + + PQXX_CHECK(!(f2.to(i)), "to(int) failed to report a null."); + PQXX_CHECK(!(f2.to(i, 54)), "to(int, int) failed to report a null."); + PQXX_CHECK_EQUAL(i, 54, "to(int, int) failed to default."); + + auto const r3{tx.exec("SELECT generate_series(1, 5)")}; + PQXX_CHECK_EQUAL(r3.at(3, 0).as(), 4, "Two-argument at() went wrong."); +#if defined(PQXX_HAVE_MULTIDIMENSIONAL_SUBSCRIPT) + PQXX_CHECK_EQUAL((r3[3, 0].as()), 4, "Two-argument [] went wrong."); +#endif +} + + +PQXX_REGISTER_TEST(test_field); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_float.cxx b/ext/libpqxx-7.7.3/test/unit/test_float.cxx new file mode 100644 index 000000000..a0463e4d0 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_float.cxx @@ -0,0 +1,175 @@ +#include + +#include + +#include "../test_helpers.hxx" + +namespace +{ +/// Test conversions for some floating-point type. +template void infinity_test() +{ + T inf{std::numeric_limits::infinity()}; + std::string inf_string; + T back_conversion; + + inf_string = pqxx::to_string(inf); + pqxx::from_string(inf_string, back_conversion); + PQXX_CHECK_LESS( + T(999999999), back_conversion, + "Infinity doesn't convert back to something huge."); + + inf_string = pqxx::to_string(-inf); + pqxx::from_string(inf_string, back_conversion); + PQXX_CHECK_LESS( + back_conversion, -T(999999999), "Negative infinity is broken"); +} + +void test_infinities() +{ + infinity_test(); + infinity_test(); + infinity_test(); +} + + +/// Reproduce bug #262: repeated float conversions break without charconv. +template void bug_262() +{ + pqxx::connection conn; + conn.prepare("stmt", "select cast($1 as float)"); + pqxx::work tr{conn}; + + // We must use the same float type both for passing the value to the + // statement and for retrieving result of the statement execution. This is + // due to an internal stringstream being instantiated as a a parameterized + // thread-local singleton. So, there are separate stream, + // stream, stream, but every such instance is a + // singleton. We should use only one of them for this test. + + pqxx::row row; + + // Nothing bad here, select a float value. + // The stream is clear, so just fill it with the value and extract str(). + row = tr.exec1("SELECT 1.0"); + + // This works properly, but as we parse the value from the stream, the + // seeking cursor moves towards the EOF. When the inevitable EOF happens + // 'eof' flag is set in the stream and 'good' flag is unset. + row[0].as(); + + // The second try. Select a float value again. The stream is not clean, so + // we need to put an empty string into its buffer {stream.str("");}. This + // resets the seeking cursor to 0. Then we will put the value using + // operator<<(). + // ... + // ... + // OOPS. stream.str("") does not reset 'eof' flag and 'good' flag! We are + // trying to read from EOF! This is no good. + // Throws on unpatched pqxx v6.4.5 + row = tr.exec1("SELECT 2.0"); + + // We won't get here without patch. The following statements are just for + // demonstration of how are intended to work. If we + // simply just reset the stream flags properly, this would work fine. + // The most obvious patch is just explicitly stream.seekg(0). + row[0].as(); + row = tr.exec1("SELECT 3.0"); + row[0].as(); +} + + +/// Test for bug #262. +void test_bug_262() +{ + bug_262(); + bug_262(); + bug_262(); +} + + +/// Test conversion of malformed floating-point values. +void test_bad_float() +{ + float x [[maybe_unused]]; + PQXX_CHECK_THROWS( + x = pqxx::from_string(""), pqxx::conversion_error, + "Conversion of empty string to float was not caught."); + + PQXX_CHECK_THROWS( + x = pqxx::from_string("Infancy"), pqxx::conversion_error, + "Misleading infinity was not caught."); + PQXX_CHECK_THROWS( + x = pqxx::from_string("-Infighting"), pqxx::conversion_error, + "Misleading negative infinity was not caught."); + + PQXX_CHECK_THROWS( + x = pqxx::from_string("Nanny"), pqxx::conversion_error, + "Conversion of misleading NaN was not caught."); +} + + +template void test_float_length(T value) +{ + auto const text{pqxx::to_string(value)}; + PQXX_CHECK_GREATER_EQUAL( + pqxx::size_buffer(value), std::size(text) + 1, + "Not enough buffer space for " + text + "."); +} + + +/// Test conversion of long float values to strings. +void test_long_float() +{ + test_float_length(0.1f); + test_float_length(0.1); + + test_float_length(std::numeric_limits::denorm_min()); + test_float_length(-std::numeric_limits::denorm_min()); + test_float_length(std::numeric_limits::min()); + test_float_length(-std::numeric_limits::min()); + test_float_length(std::numeric_limits::max()); + test_float_length(-std::numeric_limits::max()); + test_float_length(-std::nextafter(1.0f, 2.0f)); + + test_float_length(std::numeric_limits::denorm_min()); + test_float_length(-std::numeric_limits::denorm_min()); + test_float_length(std::numeric_limits::min()); + test_float_length(-std::numeric_limits::min()); + test_float_length(std::numeric_limits::max()); + test_float_length(-std::numeric_limits::max()); + test_float_length(-std::nextafter(1.0, 2.0)); + + test_float_length(std::numeric_limits::denorm_min()); + test_float_length(-std::numeric_limits::denorm_min()); + test_float_length(std::numeric_limits::min()); + test_float_length(-std::numeric_limits::min()); + test_float_length(std::numeric_limits::max()); + test_float_length(-std::numeric_limits::max()); + test_float_length(-std::nextafter(1.0L, 2.0L)); + + // Ahem. I'm not proud of this. We really can't assume much about the + // floating-point types, but I'd really like to try a few things to see that + // buffer sizes are in the right ballpark. So, if "double" is at least 64 + // bits, check for some examples of long conversions. + if constexpr (sizeof(double) >= 8) + { + auto constexpr awkward{-2.2250738585072014e-308}; + auto const text{pqxx::to_string(awkward)}; + PQXX_CHECK_LESS_EQUAL( + std::size(text), 25u, text + " converted to too long a string."); + } + if constexpr (sizeof(double) <= 8) + { + auto const text{pqxx::to_string(0.99)}; + PQXX_CHECK_LESS_EQUAL( + pqxx::size_buffer(0.99), 25u, text + " converted to too long a string."); + } +} + + +PQXX_REGISTER_TEST(test_infinities); +PQXX_REGISTER_TEST(test_bug_262); +PQXX_REGISTER_TEST(test_bad_float); +PQXX_REGISTER_TEST(test_long_float); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_largeobject.cxx b/ext/libpqxx-7.7.3/test/unit/test_largeobject.cxx new file mode 100644 index 000000000..8184fa2f8 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_largeobject.cxx @@ -0,0 +1,58 @@ +#include +#include + +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_stream_large_object() +{ + pqxx::connection conn; + + // Construct a really nasty string. (Don't just construct a std::string from + // a char[] constant, because it'll terminate at the embedded zero.) + // + // The crucial thing is the "ff" byte at the beginning. It tests for + // possible conflation between "eof" (-1) and a char which just happens to + // have the same bit pattern as an 8-bit value of -1. This conflation can be + // a problem when it occurs at buffer boundaries. + constexpr char bytes[]{"\xff\0end"}; + std::string const contents{bytes, std::size(bytes)}; + + pqxx::work tx{conn}; +#include "pqxx/internal/ignore-deprecated-pre.hxx" + pqxx::largeobject new_obj{tx}; + + pqxx::olostream write{tx, new_obj}; + write << contents; + write.flush(); + + pqxx::largeobjectaccess check{tx, new_obj, std::ios::in | std::ios::binary}; + std::array buf; + std::size_t const len{ + static_cast(check.read(std::data(buf), std::size(buf)))}; + PQXX_CHECK_EQUAL(len, std::size(contents), "olostream truncated data."); + std::string const check_str{std::data(buf), len}; + PQXX_CHECK_EQUAL(check_str, contents, "olostream mangled data."); + + pqxx::ilostream read{tx, new_obj}; + std::string read_back; + std::string chunk; + while (read >> chunk) read_back += chunk; + + new_obj.remove(tx); + + PQXX_CHECK_EQUAL(read_back, contents, "Got wrong data from ilostream."); + PQXX_CHECK_EQUAL( + std::size(read_back), std::size(contents), "ilostream truncated data."); + PQXX_CHECK_EQUAL( + std::size(read_back), std::size(bytes), "ilostream truncated data."); +#include "pqxx/internal/ignore-deprecated-post.hxx" +} + + +PQXX_REGISTER_TEST(test_stream_large_object); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_nonblocking_connect.cxx b/ext/libpqxx-7.7.3/test/unit/test_nonblocking_connect.cxx new file mode 100644 index 000000000..d1a32ac64 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_nonblocking_connect.cxx @@ -0,0 +1,27 @@ +#include + +#include + +#include "../test_helpers.hxx" + + +namespace +{ +void test_nonblocking_connect() +{ + pqxx::connecting nbc; + while (not nbc.done()) + { + pqxx::internal::wait_fd( + nbc.sock(), nbc.wait_to_read(), nbc.wait_to_write()); + nbc.process(); + } + + pqxx::connection conn{std::move(nbc).produce()}; + pqxx::work tx{conn}; + PQXX_CHECK_EQUAL(tx.query_value("SELECT 10"), 10, "Bad value!?"); +} + + +PQXX_REGISTER_TEST(test_nonblocking_connect); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_notification.cxx b/ext/libpqxx-7.7.3/test/unit/test_notification.cxx new file mode 100644 index 000000000..6487169b4 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_notification.cxx @@ -0,0 +1,86 @@ +#include + +#include + +#include + +#include + +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +class TestReceiver final : public pqxx::notification_receiver +{ +public: + std::string payload; + int backend_pid; + + TestReceiver(pqxx::connection &c, std::string const &channel_name) : + pqxx::notification_receiver(c, channel_name), + payload(), + backend_pid(0) + {} + + virtual void + operator()(std::string const &payload_string, int backend) override + { + this->payload = payload_string; + this->backend_pid = backend; + } +}; + + +void test_receive( + pqxx::transaction_base &t, std::string const &channel, + char const payload[] = nullptr) +{ + pqxx::connection &conn(t.conn()); + + std::string SQL{"NOTIFY \"" + channel + "\""}; + if (payload != nullptr) + SQL += ", " + t.quote(payload); + + TestReceiver receiver{t.conn(), channel}; + + // Clear out any previously pending notifications that might otherwise + // confuse the test. + conn.get_notifs(); + + // Notify, and receive. + t.exec(SQL); + t.commit(); + + int notifs{0}; + for (int i{0}; (i < 10) and (notifs == 0); + ++i, pqxx::internal::wait_for(1'000'000u)) + notifs = conn.get_notifs(); + + PQXX_CHECK_EQUAL(notifs, 1, "Got wrong number of notifications."); + PQXX_CHECK_EQUAL(receiver.backend_pid, conn.backendpid(), "Bad pid."); + if (payload == nullptr) + PQXX_CHECK(std::empty(receiver.payload), "Unexpected payload."); + else + PQXX_CHECK_EQUAL(receiver.payload, payload, "Bad payload."); +} + + +void test_notification() +{ + pqxx::connection conn; + TestReceiver receiver(conn, "mychannel"); + PQXX_CHECK_EQUAL(receiver.channel(), "mychannel", "Bad channel."); + + pqxx::work tx{conn}; + test_receive(tx, "channel1"); + + pqxx::nontransaction u(conn); + test_receive(u, "channel2", "payload"); +} + + +PQXX_REGISTER_TEST(test_notification); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_pipeline.cxx b/ext/libpqxx-7.7.3/test/unit/test_pipeline.cxx new file mode 100644 index 000000000..63b97662b --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_pipeline.cxx @@ -0,0 +1,64 @@ +#include + +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_pipeline() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + + // A pipeline grabs transaction focus, blocking regular queries and such. + pqxx::pipeline pipe(tx, "test_pipeline_detach"); + PQXX_CHECK_THROWS( + tx.exec("SELECT 1"), std::logic_error, + "Pipeline does not block regular queries"); + + // Flushing a pipeline relinquishes transaction focus. + pipe.flush(); + auto r{tx.exec("SELECT 2")}; + PQXX_CHECK_EQUAL( + std::size(r), 1, "Wrong query result after flushing pipeline."); + PQXX_CHECK_EQUAL( + r[0][0].as(), 2, "Query returns wrong data after flushing pipeline."); + + // Inserting a query makes the pipeline grab transaction focus back. + auto q{pipe.insert("SELECT 2")}; + PQXX_CHECK_THROWS( + tx.exec("SELECT 3"), std::logic_error, + "Pipeline does not block regular queries"); + + // Invoking complete() also detaches the pipeline from the transaction. + pipe.complete(); + r = tx.exec("SELECT 4"); + PQXX_CHECK_EQUAL(std::size(r), 1, "Wrong query result after complete()."); + PQXX_CHECK_EQUAL( + r[0][0].as(), 4, "Query returns wrong data after complete()."); + + // The complete() also received any pending query results from the backend. + r = pipe.retrieve(q); + PQXX_CHECK_EQUAL(std::size(r), 1, "Wrong result from pipeline."); + PQXX_CHECK_EQUAL(r[0][0].as(), 2, "Pipeline returned wrong data."); + + // We can cancel while the pipe is empty, and things will still work. + pipe.cancel(); + + // Issue a query and cancel it. Measure time to see that we don't really + // wait. + using clock = std::chrono::steady_clock; + auto const start{clock::now()}; + pipe.retain(0); + pipe.insert("pg_sleep(10)"); + pipe.cancel(); + auto const finish{clock::now()}; + auto const seconds{ + std::chrono::duration_cast(finish - start).count()}; + PQXX_CHECK_LESS(seconds, 5, "Canceling a sleep took suspiciously long."); +} +} // namespace + +PQXX_REGISTER_TEST(test_pipeline); diff --git a/ext/libpqxx-7.7.3/test/unit/test_prepared_statement.cxx b/ext/libpqxx-7.7.3/test/unit/test_prepared_statement.cxx new file mode 100644 index 000000000..c9eb4ee84 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_prepared_statement.cxx @@ -0,0 +1,334 @@ +#include +#include +#include +#include + +#include + +#include "../test_helpers.hxx" + +// Test program for libpqxx. Define and use prepared statements. + +#define COMPARE_RESULTS(name, lhs, rhs) \ + PQXX_CHECK_EQUAL( \ + rhs, lhs, \ + "Executing " name " as prepared statement yields different results."); + +namespace +{ +using namespace std::literals; + + +template std::string stringize(pqxx::transaction_base &t, T i) +{ + return stringize(t, pqxx::to_string(i)); +} + + +// Substitute variables in raw query. This is not likely to be very robust, +// but it should do for just this test. The main shortcomings are escaping, +// and not knowing when to quote the variables. +// Note we do the replacement backwards (meaning forward_only iterators won't +// do!) to avoid substituting e.g. "$12" as "$1" first. +template +std::string +subst(pqxx::transaction_base &t, std::string q, ITER patbegin, ITER patend) +{ + ptrdiff_t i{distance(patbegin, patend)}; + for (ITER arg{patend}; i > 0; --i) + { + --arg; + std::string const marker{"$" + pqxx::to_string(i)}, + var{stringize(t, *arg)}; + std::string::size_type const msz{std::size(marker)}; + while (q.find(marker) != std::string::npos) + q.replace(q.find(marker), msz, var); + } + return q; +} + + +template +std::string +subst(pqxx::transaction_base &t, std::string const &q, CNTNR const &patterns) +{ + return subst(t, q, std::begin(patterns), std::end(patterns)); +} + + +void test_registration_and_invocation() +{ + constexpr auto count_to_5{"SELECT * FROM generate_series(1, 5)"}; + + pqxx::connection c; + pqxx::work tx1{c}; + + // Prepare a simple statement. + tx1.conn().prepare("CountToFive", count_to_5); + + // The statement returns exactly what you'd expect. + COMPARE_RESULTS( + "CountToFive", tx1.exec_prepared("CountToFive"), tx1.exec(count_to_5)); + + // Re-preparing it is an error. + PQXX_CHECK_THROWS( + tx1.conn().prepare("CountToFive", count_to_5), pqxx::sql_error, + "Did not report re-definition of prepared statement."); + + tx1.abort(); + pqxx::work tx2{c}; + + // Executing a nonexistent prepared statement is also an error. + PQXX_CHECK_THROWS( + tx2.exec_prepared("NonexistentStatement"), pqxx::sql_error, + "Did not report invocation of nonexistent prepared statement."); +} + + +void test_basic_args() +{ + pqxx::connection c; + c.prepare("EchoNum", "SELECT $1::int"); + pqxx::work tx{c}; + auto r{tx.exec_prepared("EchoNum", 7)}; + PQXX_CHECK_EQUAL( + std::size(r), 1, "Did not get 1 row from prepared statement."); + PQXX_CHECK_EQUAL(std::size(r.front()), 1, "Did not get exactly one column."); + PQXX_CHECK_EQUAL(r[0][0].as(), 7, "Got wrong result."); + + auto rw{tx.exec_prepared1("EchoNum", 8)}; + PQXX_CHECK_EQUAL( + std::size(rw), 1, "Did not get 1 column from exec_prepared1."); + PQXX_CHECK_EQUAL(rw[0].as(), 8, "Got wrong result."); +} + + +void test_multiple_params() +{ + pqxx::connection c; + c.prepare("CountSeries", "SELECT * FROM generate_series($1::int, $2::int)"); + pqxx::work tx{c}; + auto r{tx.exec_prepared_n(4, "CountSeries", 7, 10)}; + PQXX_CHECK_EQUAL( + std::size(r), 4, "Wrong number of rows, but no error raised."); + PQXX_CHECK_EQUAL(r.front().front().as(), 7, "Wrong $1."); + PQXX_CHECK_EQUAL(r.back().front().as(), 10, "Wrong $2."); + + c.prepare("Reversed", "SELECT * FROM generate_series($2::int, $1::int)"); + r = tx.exec_prepared_n(3, "Reversed", 8, 6); + PQXX_CHECK_EQUAL( + r.front().front().as(), 6, "Did parameters get reordered?"); + PQXX_CHECK_EQUAL( + r.back().front().as(), 8, "$2 did not come through properly."); +} + + +void test_nulls() +{ + pqxx::connection c; + pqxx::work tx{c}; + c.prepare("EchoStr", "SELECT $1::varchar"); + auto rw{tx.exec_prepared1("EchoStr", nullptr)}; + PQXX_CHECK(rw.front().is_null(), "nullptr did not translate to null."); + + char const *n{nullptr}; + rw = tx.exec_prepared1("EchoStr", n); + PQXX_CHECK(rw.front().is_null(), "Null pointer did not translate to null."); +} + + +void test_strings() +{ + pqxx::connection c; + pqxx::work tx{c}; + c.prepare("EchoStr", "SELECT $1::varchar"); + auto rw{tx.exec_prepared1("EchoStr", "foo")}; + PQXX_CHECK_EQUAL( + rw.front().as(), "foo", "Wrong string result."); + + char const nasty_string[]{R"--('\"\)--"}; + rw = tx.exec_prepared1("EchoStr", nasty_string); + PQXX_CHECK_EQUAL( + rw.front().as(), std::string(nasty_string), + "Prepared statement did not quote/escape correctly."); + + rw = tx.exec_prepared1("EchoStr", std::string{nasty_string}); + PQXX_CHECK_EQUAL( + rw.front().as(), std::string(nasty_string), + "Quoting/escaping went wrong in std::string."); + + char nonconst[]{"non-const C string"}; + rw = tx.exec_prepared1("EchoStr", nonconst); + PQXX_CHECK_EQUAL( + rw.front().as(), std::string(nonconst), + "Non-const C string passed incorrectly."); +} + + +void test_binary() +{ + pqxx::connection c; + pqxx::work tx{c}; + c.prepare("EchoBin", "SELECT $1::bytea"); + constexpr char raw_bytes[]{"Binary\0bytes'\"with\tweird\xff bytes"}; + std::string const input{raw_bytes, std::size(raw_bytes)}; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + { + pqxx::binarystring const bin{input}; + auto rw{tx.exec_prepared1("EchoBin", bin)}; + PQXX_CHECK_EQUAL( + pqxx::binarystring(rw[0]).str(), input, + "Binary string came out damaged."); + } +#include "pqxx/internal/ignore-deprecated-post.hxx" + + { + std::basic_string bytes{ + reinterpret_cast(raw_bytes), std::size(raw_bytes)}; + auto bp{tx.exec_prepared1("EchoBin", bytes)}; + auto bval{bp[0].as>()}; + PQXX_CHECK_EQUAL( + (std::string_view{ + reinterpret_cast(bval.c_str()), std::size(bval)}), + input, "Binary string parameter went wrong."); + } + + // Now try it with a complex type that ultimately uses the conversions of + // std::basic_string, but complex enough that the call may + // convert the data to a text string on the libpqxx side. Which would be + // okay, except of course it's likely to be slower. + + { + auto ptr{std::make_shared>( + reinterpret_cast(raw_bytes), std::size(raw_bytes))}; + auto rp{tx.exec_prepared1("EchoBin", ptr)}; + auto pval{rp[0].as>()}; + PQXX_CHECK_EQUAL( + (std::string_view{ + reinterpret_cast(pval.c_str()), std::size(pval)}), + input, "Binary string as shared_ptr-to-optional went wrong."); + } + + { + auto opt{std::optional>{ + std::in_place, reinterpret_cast(raw_bytes), + std::size(raw_bytes)}}; + auto op{tx.exec_prepared1("EchoBin", opt)}; + auto oval{op[0].as>()}; + PQXX_CHECK_EQUAL( + (std::string_view{ + reinterpret_cast(oval.c_str()), std::size(oval)}), + input, "Binary string as shared_ptr-to-optional went wrong."); + } + +#if defined(PQXX_HAVE_CONCEPTS) + // By the way, it doesn't have to be a std::basic_string. Any contiguous + // range will do. + { + std::vector data{std::byte{'x'}, std::byte{'v'}}; + auto op{tx.exec_prepared1("EchoBin", data)}; + auto oval{op[0].as>()}; + PQXX_CHECK_EQUAL( + std::size(oval), 2u, "Binary data came back as wrong length."); + PQXX_CHECK_EQUAL(static_cast(oval[0]), int('x'), "Wrong data."); + PQXX_CHECK_EQUAL(static_cast(oval[1]), int('v'), "Wrong data."); + } +#endif +} + + +void test_params() +{ + pqxx::connection c; + pqxx::work tx{c}; + c.prepare("Concat2Numbers", "SELECT 10 * $1 + $2"); + std::vector values{3, 9}; + pqxx::params params; + params.reserve(std::size(values)); + params.append_multi(values); + + auto const rw39{tx.exec_prepared1("Concat2Numbers", params)}; + PQXX_CHECK_EQUAL( + rw39.front().as(), 39, + "Dynamic prepared-statement parameters went wrong."); + + c.prepare("Concat4Numbers", "SELECT 1000*$1 + 100*$2 + 10*$3 + $4"); + auto const rw1396{tx.exec_prepared1("Concat4Numbers", 1, params, 6)}; + PQXX_CHECK_EQUAL( + rw1396.front().as(), 1396, + "Dynamic params did not interleave with static ones properly."); +} + + +void test_optional() +{ + pqxx::connection c; + pqxx::work tx{c}; + c.prepare("EchoNum", "SELECT $1::int"); + pqxx::row rw{ + tx.exec_prepared1("EchoNum", std::optional{std::in_place, 10})}; + PQXX_CHECK_EQUAL( + rw.front().as(), 10, + "optional (with value) did not return the right value."); + + rw = tx.exec_prepared1("EchoNum", std::optional{}); + PQXX_CHECK( + rw.front().is_null(), "optional without value did not come out as null."); +} + + +void test_prepared_statements() +{ + test_registration_and_invocation(); + test_basic_args(); + test_multiple_params(); + test_nulls(); + test_strings(); + test_binary(); + test_params(); + + test_optional(); +} + + +void test_placeholders_generates_names() +{ + using pqxx::operator""_zv; + pqxx::placeholders name; + PQXX_CHECK_EQUAL(name.view(), "$1"_zv, "Bad placeholders initial zview."); + PQXX_CHECK_EQUAL(name.view(), "$1"sv, "Bad placeholders string_view."); + PQXX_CHECK_EQUAL(name.get(), "$1", "Bad placeholders::get()."); + + name.next(); + PQXX_CHECK_EQUAL(name.view(), "$2"_zv, "Incorrect placeholders::next()."); + + name.next(); + PQXX_CHECK_EQUAL(name.view(), "$3"_zv, "Incorrect placeholders::next()."); + name.next(); + PQXX_CHECK_EQUAL(name.view(), "$4"_zv, "Incorrect placeholders::next()."); + name.next(); + PQXX_CHECK_EQUAL(name.view(), "$5"_zv, "Incorrect placeholders::next()."); + name.next(); + PQXX_CHECK_EQUAL(name.view(), "$6"_zv, "Incorrect placeholders::next()."); + name.next(); + PQXX_CHECK_EQUAL(name.view(), "$7"_zv, "Incorrect placeholders::next()."); + name.next(); + PQXX_CHECK_EQUAL(name.view(), "$8"_zv, "Incorrect placeholders::next()."); + name.next(); + PQXX_CHECK_EQUAL(name.view(), "$9"_zv, "Incorrect placeholders::next()."); + name.next(); + PQXX_CHECK_EQUAL(name.view(), "$10"_zv, "Incorrect placeholders carry."); + name.next(); + PQXX_CHECK_EQUAL(name.view(), "$11"_zv, "Incorrect placeholders 11."); + + while (name.count() < 999) name.next(); + PQXX_CHECK_EQUAL(name.view(), "$999"_zv, "Incorrect placeholders 999."); + name.next(); + PQXX_CHECK_EQUAL(name.view(), "$1000"_zv, "Incorrect large placeholder."); +} + + +PQXX_REGISTER_TEST(test_prepared_statements); +PQXX_REGISTER_TEST(test_placeholders_generates_names); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_range.cxx b/ext/libpqxx-7.7.3/test/unit/test_range.cxx new file mode 100644 index 000000000..b40d9b043 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_range.cxx @@ -0,0 +1,555 @@ +#include +#include + +#include "../test_helpers.hxx" + + +namespace +{ +void test_range_construct() +{ + using optint = std::optional; + using oibound = pqxx::inclusive_bound>; + using oxbound = pqxx::inclusive_bound>; + PQXX_CHECK_THROWS( + (pqxx::range{oibound{optint{}}, oibound{optint{}}}), + pqxx::argument_error, "Inclusive bound accepted a null."); + PQXX_CHECK_THROWS( + (pqxx::range{oxbound{optint{}}, oxbound{optint{}}}), + pqxx::argument_error, "Exclusive bound accepted a null."); + + using ibound = pqxx::inclusive_bound; + PQXX_CHECK_THROWS( + (pqxx::range{ibound{1}, ibound{0}}), pqxx::range_error, + "Range constructor accepted backwards range."); + + PQXX_CHECK_THROWS( + (pqxx::range{ + pqxx::inclusive_bound{-1000.0}, + pqxx::inclusive_bound{-std::numeric_limits::infinity()}}), + pqxx::range_error, + "Was able to construct range with infinity bound at the wrong end."); +} + + +void test_range_equality() +{ + using range = pqxx::range; + using ibound = pqxx::inclusive_bound; + using xbound = pqxx::exclusive_bound; + using ubound = pqxx::no_bound; + + PQXX_CHECK_EQUAL( + range{}, range{}, "Default-constructed range is not consistent."); + PQXX_CHECK_EQUAL( + (range{xbound{0}, xbound{0}}), (range{xbound{5}, xbound{5}}), + "Empty ranges at different values are not equal."); + + PQXX_CHECK_EQUAL( + (range{ubound{}, ubound{}}), (range{ubound{}, ubound{}}), + "Universal range is inconsistent."); + PQXX_CHECK_EQUAL( + (range{ibound{5}, ibound{8}}), (range{ibound{5}, ibound{8}}), + "Inclusive range is inconsistent."); + PQXX_CHECK_EQUAL( + (range{xbound{5}, xbound{8}}), (range{xbound{5}, xbound{8}}), + "Exclusive range is inconsistent."); + PQXX_CHECK_EQUAL( + (range{xbound{5}, ibound{8}}), (range{xbound{5}, ibound{8}}), + "Left-exclusive interval is not equal to itself."); + PQXX_CHECK_EQUAL( + (range{ibound{5}, xbound{8}}), (range{ibound{5}, xbound{8}}), + "Right-exclusive interval is not equal to itself."); + PQXX_CHECK_EQUAL( + (range{ubound{}, ibound{8}}), (range{ubound{}, ibound{8}}), + "Unlimited lower bound does not compare equal to same."); + PQXX_CHECK_EQUAL( + (range{ibound{8}, ubound{}}), (range{ibound{8}, ubound{}}), + "Unlimited upper bound does not compare equal to same."); + + PQXX_CHECK_NOT_EQUAL( + (range{ibound{5}, ibound{8}}), (range{xbound{5}, ibound{8}}), + "Equality does not detect inclusive vs. exclusive lower bound."); + PQXX_CHECK_NOT_EQUAL( + (range{ibound{5}, ibound{8}}), (range{ubound{}, ibound{8}}), + "Equality does not detect inclusive vs. unlimited lower bound."); + PQXX_CHECK_NOT_EQUAL( + (range{xbound{5}, ibound{8}}), (range{ubound{}, ibound{8}}), + "Equality does not detect exclusive vs. unlimited lower bound."); + PQXX_CHECK_NOT_EQUAL( + (range{ibound{5}, ibound{8}}), (range{ibound{5}, xbound{8}}), + "Equality does not detect inclusive vs. exclusive upper bound."); + PQXX_CHECK_NOT_EQUAL( + (range{ibound{5}, ibound{8}}), (range{ibound{5}, ubound{}}), + "Equality does not detect inclusive vs. unlimited upper bound."); + PQXX_CHECK_NOT_EQUAL( + (range{ibound{5}, xbound{8}}), (range{ibound{5}, ubound{}}), + "Equality does not detect exclusive vs. unlimited upper bound."); + + PQXX_CHECK_NOT_EQUAL( + (range{ibound{5}, ibound{8}}), (range{ibound{4}, ibound{8}}), + "Equality does not compare lower inclusive bound value."); + PQXX_CHECK_NOT_EQUAL( + (range{xbound{5}, ibound{8}}), (range{xbound{4}, ibound{8}}), + "Equality does not compare lower exclusive bound value."); + PQXX_CHECK_NOT_EQUAL( + (range{xbound{5}, ibound{8}}), (range{xbound{5}, ibound{7}}), + "Equality does not compare upper inclusive bound value."); + PQXX_CHECK_NOT_EQUAL( + (range{xbound{5}, xbound{8}}), (range{xbound{5}, xbound{7}}), + "Equality does not compare lower exclusive bound value."); +} + + +void test_range_empty() +{ + using range = pqxx::range; + using ibound = pqxx::inclusive_bound; + using xbound = pqxx::exclusive_bound; + using ubound = pqxx::no_bound; + PQXX_CHECK((range{}.empty()), "Default-constructed range is not empty."); + PQXX_CHECK( + (range{ibound{10}, xbound{10}}).empty(), + "Right-exclusive zero-length interval is not empty."); + PQXX_CHECK( + (range{xbound{10}, ibound{10}}).empty(), + "Left-exclusive zero-length interval is not empty."); + PQXX_CHECK( + (range{xbound{10}, xbound{10}}).empty(), + "Exclusive zero-length interval is not empty."); + + PQXX_CHECK( + not(range{ibound{10}, ibound{10}}).empty(), + "Inclusive zero-length interval is empty."); + PQXX_CHECK( + not(range{xbound{10}, ibound{11}}.empty()), + "Interval is incorrectly empty."); + PQXX_CHECK( + not(range{ubound{}, ubound{}}.empty()), + "Double-unlimited interval is empty."); + PQXX_CHECK( + not(range{ubound{}, xbound{0}}.empty()), + "Left-unlimited interval is empty."); + PQXX_CHECK( + not(range{xbound{0}, ubound{}}.empty()), + "Right-unlimited interval is empty."); +} + + +void test_range_contains() +{ + using range = pqxx::range; + using ibound = pqxx::inclusive_bound; + using xbound = pqxx::exclusive_bound; + using ubound = pqxx::no_bound; + + PQXX_CHECK(not(range{}.contains(-1)), "Empty range contains a value."); + PQXX_CHECK(not(range{}.contains(0)), "Empty range contains a value."); + PQXX_CHECK(not(range{}.contains(1)), "Empty range contains a value."); + + PQXX_CHECK( + not(range{ibound{5}, ibound{8}}.contains(4)), + "Inclusive range contains value outside its left bound."); + PQXX_CHECK( + (range{ibound{5}, ibound{8}}.contains(5)), + "Inclusive range does not contain value on its left bound."); + PQXX_CHECK( + (range{ibound{5}, ibound{8}}.contains(6)), + "Inclusive range does not contain value inside it."); + PQXX_CHECK( + (range{ibound{5}, ibound{8}}.contains(8)), + "Inclusive range does not contain value on its right bound."); + PQXX_CHECK( + not(range{ibound{5}, ibound{8}}.contains(9)), + "Inclusive range contains value outside its right bound."); + + PQXX_CHECK( + not(range{ibound{5}, xbound{8}}.contains(4)), + "Left-inclusive range contains value outside its left bound."); + PQXX_CHECK( + (range{ibound{5}, xbound{8}}.contains(5)), + "Left-inclusive range does not contain value on its left bound."); + PQXX_CHECK( + (range{ibound{5}, xbound{8}}.contains(6)), + "Left-inclusive range does not contain value inside it."); + PQXX_CHECK( + not(range{ibound{5}, xbound{8}}.contains(8)), + "Left-inclusive range contains value on its right bound."); + PQXX_CHECK( + not(range{ibound{5}, xbound{8}}.contains(9)), + "Left-inclusive range contains value outside its right bound."); + + PQXX_CHECK( + not(range{xbound{5}, ibound{8}}.contains(4)), + "Right-inclusive range contains value outside its left bound."); + PQXX_CHECK( + not(range{xbound{5}, ibound{8}}.contains(5)), + "Right-inclusive range does contains value on its left bound."); + PQXX_CHECK( + (range{xbound{5}, ibound{8}}.contains(6)), + "Right-inclusive range does not contain value inside it."); + PQXX_CHECK( + (range{xbound{5}, ibound{8}}.contains(8)), + "Right-inclusive range does not contain value on its right bound."); + PQXX_CHECK( + not(range{xbound{5}, ibound{8}}.contains(9)), + "Right-inclusive range contains value outside its right bound."); + + PQXX_CHECK( + not(range{xbound{5}, xbound{8}}.contains(4)), + "Exclusive range contains value outside its left bound."); + PQXX_CHECK( + not(range{xbound{5}, xbound{8}}.contains(5)), + "Exclusive range contains value on its left bound."); + PQXX_CHECK( + (range{xbound{5}, xbound{8}}.contains(6)), + "Exclusive range does not contain value inside it."); + PQXX_CHECK( + not(range{xbound{5}, xbound{8}}.contains(8)), + "Exclusive range does contains value on its right bound."); + PQXX_CHECK( + not(range{xbound{5}, xbound{8}}.contains(9)), + "Exclusive range contains value outside its right bound."); + + PQXX_CHECK( + (range{ubound{}, ibound{8}}.contains(7)), + "Right-inclusive range does not contain value inside it."); + PQXX_CHECK( + (range{ubound{}, ibound{8}}.contains(8)), + "Right-inclusive range does not contain value on its right bound."); + PQXX_CHECK( + not(range{ubound{}, ibound{8}}.contains(9)), + "Right-inclusive range contains value outside its right bound."); + + PQXX_CHECK( + (range{ubound{}, xbound{8}}.contains(7)), + "Right-exclusive range does not contain value inside it."); + PQXX_CHECK( + not(range{ubound{}, xbound{8}}.contains(8)), + "Right-exclusive range contains value on its right bound."); + PQXX_CHECK( + not(range{ubound{}, xbound{8}}.contains(9)), + "Right-exclusive range contains value outside its right bound."); + + PQXX_CHECK( + not(range{ibound{5}, ubound{}}.contains(4)), + "Left-inclusive range contains value outside its left bound."); + PQXX_CHECK( + (range{ibound{5}, ubound{}}.contains(5)), + "Left-inclusive range does not contain value on its left bound."); + PQXX_CHECK( + (range{ibound{5}, ubound{}}.contains(6)), + "Left-inclusive range does not contain value inside it."); + + PQXX_CHECK( + not(range{xbound{5}, ubound{}}.contains(4)), + "Left-exclusive range contains value outside its left bound."); + PQXX_CHECK( + not(range{xbound{5}, ubound{}}.contains(5)), + "Left-exclusive range contains value on its left bound."); + PQXX_CHECK( + (range{xbound{5}, ubound{}}.contains(6)), + "Left-exclusive range does not contain value inside it."); + + PQXX_CHECK( + (range{ubound{}, ubound{}}.contains(-1)), "Value not in universal range."); + PQXX_CHECK( + (range{ubound{}, ubound{}}.contains(0)), "Value not in universal range."); + PQXX_CHECK( + (range{ubound{}, ubound{}}.contains(1)), "Value not in universal range."); +} + + +void test_float_range_contains() +{ + using range = pqxx::range; + using ibound = pqxx::inclusive_bound; + using xbound = pqxx::exclusive_bound; + using ubound = pqxx::no_bound; + using limits = std::numeric_limits; + constexpr auto inf{limits::infinity()}; + + PQXX_CHECK( + not(range{ibound{4.0}, ibound{8.0}}.contains(3.9)), + "Float inclusive range contains value beyond its lower bound."); + PQXX_CHECK( + (range{ibound{4.0}, ibound{8.0}}.contains(4.0)), + "Float inclusive range does not contain its lower bound value."); + PQXX_CHECK( + (range{ibound{4.0}, ibound{8.0}}.contains(5.0)), + "Float inclusive range does not contain value inside it."); + + PQXX_CHECK( + (range{ibound{0}, ibound{inf}}).contains(9999.0), + "Range to infinity did not include large number."); + PQXX_CHECK( + not(range{ibound{0}, ibound{inf}}.contains(-0.1)), + "Range to infinity includes number outside it."); + PQXX_CHECK( + (range{ibound{0}, xbound{inf}}.contains(9999.0)), + "Range to exclusive infinity did not include large number."); + PQXX_CHECK( + (range{ibound{0}, ibound{inf}}).contains(inf), + "Range to inclusive infinity does not include infinity."); + PQXX_CHECK( + not(range{ibound{0}, xbound{inf}}.contains(inf)), + "Range to exclusive infinity includes infinity."); + PQXX_CHECK( + (range{ibound{0}, ubound{}}).contains(inf), + "Right-unlimited range does not include infinity."); + + PQXX_CHECK( + (range{ibound{-inf}, ibound{0}}).contains(-9999.0), + "Range from infinity did not include large negative number."); + PQXX_CHECK( + not(range{ibound{-inf}, ibound{0}}.contains(0.1)), + "Range from infinity includes number outside it."); + PQXX_CHECK( + (range{xbound{-inf}, ibound{0}}).contains(-9999.0), + "Range from exclusive infinity did not include large negative number."); + PQXX_CHECK( + (range{ibound{-inf}, ibound{0}}).contains(-inf), + "Range from inclusive infinity does not include negative infinity."); + PQXX_CHECK( + not(range{xbound{-inf}, ibound{0}}).contains(-inf), + "Range to infinity exclusive includes negative infinity."); + PQXX_CHECK( + (range{ubound{}, ibound{0}}).contains(-inf), + "Left-unlimited range does not include negative infinity."); +} + + +void test_range_subset() +{ + using range = pqxx::range; + using traits = pqxx::string_traits; + + std::string_view subsets[][2]{ + {"empty", "empty"}, {"(,)", "empty"}, {"(0,1)", "empty"}, + {"(,)", "[-10,10]"}, {"(,)", "(-10,10)"}, {"(,)", "(,)"}, + {"(,10)", "(,10)"}, {"(,10)", "(,9)"}, {"(,10]", "(,10)"}, + {"(,10]", "(,10]"}, {"(1,)", "(10,)"}, {"(1,)", "(9,)"}, + {"[1,)", "(10,)"}, {"[1,)", "[10,)"}, {"[0,5]", "[1,4]"}, + {"(0,5)", "[1,4]"}, + }; + for (auto const [super, sub] : subsets) + PQXX_CHECK( + traits::from_string(super).contains(traits::from_string(sub)), + pqxx::internal::concat( + "Range '", super, "' did not contain '", sub, "'.")); + + std::string_view non_subsets[][2]{ + {"empty", "[0,0]"}, {"empty", "(,)"}, {"[-10,10]", "(,)"}, + {"(-10,10)", "(,)"}, {"(,9)", "(,10)"}, {"(,10)", "(,10]"}, + {"[1,4]", "[0,4]"}, {"[1,4]", "[1,5]"}, {"(0,10)", "[0,10]"}, + {"(0,10)", "(0,10]"}, {"(0,10)", "[0,10)"}, + }; + for (auto const [super, sub] : non_subsets) + PQXX_CHECK( + not traits::from_string(super).contains(traits::from_string(sub)), + pqxx::internal::concat("Range '", super, "' contained '", sub, "'.")); +} + + +void test_range_to_string() +{ + using range = pqxx::range; + using ibound = pqxx::inclusive_bound; + using xbound = pqxx::exclusive_bound; + using ubound = pqxx::no_bound; + + PQXX_CHECK_EQUAL( + pqxx::to_string(range{}), "empty", "Empty range came out wrong."); + + PQXX_CHECK_EQUAL( + pqxx::to_string(range{ibound{5}, ibound{8}}), "[5,8]", + "Inclusive range came out wrong."); + PQXX_CHECK_EQUAL( + pqxx::to_string(range{xbound{5}, ibound{8}}), "(5,8]", + "Left-exclusive range came out wrong."); + PQXX_CHECK_EQUAL( + pqxx::to_string(range{ibound{5}, xbound{8}}), "[5,8)", + "Right-exclusive range came out wrong."); + PQXX_CHECK_EQUAL( + pqxx::to_string(range{xbound{5}, xbound{8}}), "(5,8)", + "Exclusive range came out wrong."); + + // Unlimited boundaries can use brackets or parentheses. Doesn't matter. + // We cheat and use some white-box knowledge of our implementation here. + PQXX_CHECK_EQUAL( + pqxx::to_string(range{ubound{}, ubound{}}), "(,)", + "Universal range came out unexpected."); + PQXX_CHECK_EQUAL( + pqxx::to_string(range{ubound{}, ibound{8}}), "(,8]", + "Left-unlimited range came out unexpected."); + PQXX_CHECK_EQUAL( + pqxx::to_string(range{ubound{}, xbound{8}}), "(,8)", + "Left-unlimited range came out unexpected."); + PQXX_CHECK_EQUAL( + pqxx::to_string(range{ibound{5}, ubound{}}), "[5,)", + "Right-unlimited range came out unexpected."); + PQXX_CHECK_EQUAL( + pqxx::to_string(range{xbound{5}, ubound{}}), "(5,)", + "Right-unlimited range came out unexpected."); +} + + +void test_parse_range() +{ + using range = pqxx::range; + using ubound = pqxx::no_bound; + using traits = pqxx::string_traits; + + constexpr std::string_view empties[]{"empty", "EMPTY", "eMpTy"}; + for (auto empty : empties) + PQXX_CHECK( + traits::from_string(empty).empty(), + pqxx::internal::concat( + "This was supposed to produce an empty range: '", empty, "'")); + + constexpr std::string_view universals[]{"(,)", "[,)", "(,]", "[,]"}; + for (auto univ : universals) + PQXX_CHECK_EQUAL( + traits::from_string(univ), (range{ubound{}, ubound{}}), + pqxx::internal::concat( + "This was supposed to produce a universal range: '", univ, "'")); + + PQXX_CHECK( + traits::from_string("(0,10]").lower_bound().is_exclusive(), + "Exclusive lower bound did not parse right."); + PQXX_CHECK( + traits::from_string("[0,10]").lower_bound().is_inclusive(), + "Inclusive lower bound did not parse right."); + PQXX_CHECK( + traits::from_string("(0,10)").upper_bound().is_exclusive(), + "Exclusive upper bound did not parse right."); + PQXX_CHECK( + traits::from_string("[0,10]").upper_bound().is_inclusive(), + "Inclusive upper bound did not parse right."); + + PQXX_CHECK_EQUAL( + *traits::from_string("(\"0\",\"10\")").lower_bound().value(), 0, + "Quoted range boundary did not parse right."); + PQXX_CHECK_EQUAL( + *traits::from_string("(\"0\",\"10\")").upper_bound().value(), 10, + "Quoted upper boundary did not parse right."); + + auto floats{ + pqxx::string_traits>::from_string("(0,1.0)")}; + PQXX_CHECK_GREATER( + *floats.lower_bound().value(), -0.001, + "Float lower bound is out of range."); + PQXX_CHECK_LESS( + *floats.lower_bound().value(), 0.001, + "Float lower bound is out of range."); + PQXX_CHECK_GREATER( + *floats.upper_bound().value(), 0.999, + "Float upper bound is out of range."); + PQXX_CHECK_LESS( + *floats.upper_bound().value(), 1.001, + "Float upper bound is out of range."); +} + + +void test_parse_bad_range() +{ + using range = pqxx::range; + using conv_err = pqxx::conversion_error; + using traits = pqxx::string_traits; + constexpr std::string_view bad_ranges[]{ + "", "x", "e", "empt", "emptyy", "()", + "[]", "(empty)", "(empty, 0)", "(0, empty)", ",", "(,", + ",)", "(1,2,3)", "(4,5x)", "(null, 0)", "[0, 1.0]", "[1.0, 0]", + }; + + for (auto bad : bad_ranges) + PQXX_CHECK_THROWS( + pqxx::ignore_unused(traits::from_string(bad)), conv_err, + pqxx::internal::concat( + "This range wasn't supposed to parse: '", bad, "'")); +} + + +/// Parse ranges lhs and rhs, return their intersection as a string. +template +std::string intersect(std::string_view lhs, std::string_view rhs) +{ + using traits = pqxx::string_traits>; + return pqxx::to_string(traits::from_string(lhs) & traits::from_string(rhs)); +} + + +void test_range_intersection() +{ + // Intersections and their expected results, in text form. + // Each row contains two ranges, and their intersection. + std::string_view intersections[][3]{ + {"empty", "empty", "empty"}, + {"(,)", "empty", "empty"}, + {"[,]", "empty", "empty"}, + {"empty", "[0,10]", "empty"}, + {"(,)", "(,)", "(,)"}, + {"(,)", "(5,8)", "(5,8)"}, + {"(,)", "[5,8)", "[5,8)"}, + {"(,)", "(5,8]", "(5,8]"}, + {"(,)", "[5,8]", "[5,8]"}, + {"(-1000,10)", "(0,1000)", "(0,10)"}, + {"[-1000,10)", "(0,1000)", "(0,10)"}, + {"(-1000,10]", "(0,1000)", "(0,10]"}, + {"[-1000,10]", "(0,1000)", "(0,10]"}, + {"[0,100]", "[0,100]", "[0,100]"}, + {"[0,100]", "[0,100)", "[0,100)"}, + {"[0,100]", "(0,100]", "(0,100]"}, + {"[0,100]", "(0,100)", "(0,100)"}, + {"[0,10]", "[11,20]", "empty"}, + {"[0,10]", "(11,20]", "empty"}, + {"[0,10]", "[11,20)", "empty"}, + {"[0,10]", "(11,20)", "empty"}, + {"[0,10]", "[10,11]", "[10,10]"}, + {"[0,10)", "[10,11]", "empty"}, + {"[0,10]", "(10,11]", "empty"}, + {"[0,10)", "(10,11]", "empty"}, + }; + for (auto [left, right, expected] : intersections) + { + PQXX_CHECK_EQUAL( + intersect(left, right), expected, + pqxx::internal::concat( + "Intersection of '", left, "' and '", right, + " produced unexpected result.")); + PQXX_CHECK_EQUAL( + intersect(right, left), expected, + pqxx::internal::concat( + "Intersection of '", left, "' and '", right, " was asymmetric.")); + } +} + + +void test_range_conversion() +{ + std::string_view const ranges[]{ + "empty", "(,)", "(,10)", "(0,)", "[0,10]", "[0,10)", "(0,10]", "(0,10)", + }; + + for (auto r : ranges) + { + auto const shortr{pqxx::from_string>(r)}; + pqxx::range intr{shortr}; + PQXX_CHECK_EQUAL( + pqxx::to_string(intr), r, "Converted range looks different."); + } +} + + +PQXX_REGISTER_TEST(test_range_construct); +PQXX_REGISTER_TEST(test_range_equality); +PQXX_REGISTER_TEST(test_range_empty); +PQXX_REGISTER_TEST(test_range_contains); +PQXX_REGISTER_TEST(test_float_range_contains); +PQXX_REGISTER_TEST(test_range_subset); +PQXX_REGISTER_TEST(test_range_to_string); +PQXX_REGISTER_TEST(test_parse_range); +PQXX_REGISTER_TEST(test_parse_bad_range); +PQXX_REGISTER_TEST(test_range_intersection); +PQXX_REGISTER_TEST(test_range_conversion); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_read_transaction.cxx b/ext/libpqxx-7.7.3/test/unit/test_read_transaction.cxx new file mode 100644 index 000000000..402f2a06f --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_read_transaction.cxx @@ -0,0 +1,22 @@ +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_read_transaction() +{ + pqxx::connection conn; + pqxx::read_transaction tx{conn}; + PQXX_CHECK_EQUAL( + tx.exec("SELECT 1")[0][0].as(), 1, + "Bad result from read transaction."); + + PQXX_CHECK_THROWS( + tx.exec("CREATE TABLE should_not_exist(x integer)"), pqxx::sql_error, + "Read-only transaction allows database to be modified."); +} + + +PQXX_REGISTER_TEST(test_read_transaction); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_result_iteration.cxx b/ext/libpqxx-7.7.3/test/unit/test_result_iteration.cxx new file mode 100644 index 000000000..3d26e3c3e --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_result_iteration.cxx @@ -0,0 +1,137 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_result_iteration() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::result r{tx.exec("SELECT generate_series(1, 3)")}; + + PQXX_CHECK(std::end(r) != std::begin(r), "Broken begin/end."); + PQXX_CHECK(std::rend(r) != std::rbegin(r), "Broken rbegin/rend."); + + PQXX_CHECK(std::cbegin(r) == std::begin(r), "Wrong cbegin."); + PQXX_CHECK(std::cend(r) == std::end(r), "Wrong cend."); + PQXX_CHECK(std::crbegin(r) == std::rbegin(r), "Wrong crbegin."); + PQXX_CHECK(std::crend(r) == std::rend(r), "Wrong crend."); + + PQXX_CHECK_EQUAL(r.front().front().as(), 1, "Unexpected front()."); + PQXX_CHECK_EQUAL(r.back().front().as(), 3, "Unexpected back()."); +} + + +void test_result_iter() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::result r{tx.exec("SELECT generate_series(1, 3)")}; + + int total{0}; + for (auto const &[i] : r.iter()) total += i; + PQXX_CHECK_EQUAL(total, 6, "iter() loop did not get the right values."); +} + + +void test_result_iterator_swap() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::result r{tx.exec("SELECT generate_series(1, 3)")}; + + auto head{std::begin(r)}, next{head + 1}; + head.swap(next); + PQXX_CHECK_EQUAL(head[0].as(), 2, "Result iterator swap is wrong."); + PQXX_CHECK_EQUAL(next[0].as(), 1, "Result iterator swap is crazy."); + + auto tail{std::rbegin(r)}, prev{tail + 1}; + tail.swap(prev); + PQXX_CHECK_EQUAL(tail[0].as(), 2, "Reverse iterator swap is wrong."); + PQXX_CHECK_EQUAL(prev[0].as(), 3, "Reverse iterator swap is crazy."); +} + + +void test_result_iterator_assignment() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::result r{tx.exec("SELECT generate_series(1, 3)")}; + + pqxx::result::const_iterator fwd; + pqxx::result::const_reverse_iterator rev; + + fwd = std::begin(r); + PQXX_CHECK_EQUAL( + fwd[0].as(), std::begin(r)[0].as(), + "Result iterator assignment is wrong."); + + rev = std::rbegin(r); + PQXX_CHECK_EQUAL( + rev[0].as(), std::rbegin(r)[0].as(), + "Reverse iterator assignment is wrong."); +} + + +void check_employee(std::string name, int salary) +{ + PQXX_CHECK(name == "x" or name == "y" or name == "z", "Unknown name."); + PQXX_CHECK( + salary == 1000 or salary == 1200 or salary == 1500, "Unknown salary."); +} + + +void test_result_for_each() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + tx.exec0("CREATE TEMP TABLE employee(name varchar, salary int)"); + auto fill{pqxx::stream_to::table(tx, {"employee"}, {"name", "salary"})}; + fill.write_values("x", 1000); + fill.write_values("y", 1200); + fill.write_values("z", 1500); + fill.complete(); + + auto const res{tx.exec("SELECT name, salary FROM employee ORDER BY name")}; + + // Use for_each with a function. + res.for_each(check_employee); + + // Use for_each with a simple lambda. + res.for_each( + [](std::string name, int salary) { check_employee(name, salary); }); + + // Use for_each with a lambda closure. + std::string names{}; + int total{0}; + + res.for_each([&names, &total](std::string name, int salary) { + names.append(name); + total += salary; + }); + PQXX_CHECK_EQUAL( + names, "xyz", "result::for_each did not accumulate names correctly."); + PQXX_CHECK_EQUAL(total, 1000 + 1200 + 1500, "Salaries added up wrong."); + + // In addition to regular conversions, you can receive arguments as + // string_view, or as references. + names.clear(); + total = 0; + res.for_each([&names, &total](std::string_view &&name, int const &salary) { + names.append(name); + total += salary; + }); + PQXX_CHECK_EQUAL( + names, "xyz", "result::for_each did not accumulate names correctly."); + PQXX_CHECK_EQUAL(total, 1000 + 1200 + 1500, "Salaries added up wrong."); +} + + +PQXX_REGISTER_TEST(test_result_iteration); +PQXX_REGISTER_TEST(test_result_iter); +PQXX_REGISTER_TEST(test_result_iterator_swap); +PQXX_REGISTER_TEST(test_result_iterator_assignment); +PQXX_REGISTER_TEST(test_result_for_each); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_result_slicing.cxx b/ext/libpqxx-7.7.3/test/unit/test_result_slicing.cxx new file mode 100644 index 000000000..be2055ee3 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_result_slicing.cxx @@ -0,0 +1,157 @@ +#include + +#include "../test_helpers.hxx" + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + +namespace pqxx +{ +template<> struct nullness : no_null +{}; + +template<> +struct nullness + : no_null +{}; + + +template<> struct string_traits +{ + static constexpr zview text{"[row::const_iterator]"}; + static zview to_buf(char *, char *, row::const_iterator const &) + { + return text; + } + static char *into_buf(char *begin, char *end, row::const_iterator const &) + { + if ((end - begin) <= 30) + throw conversion_overrun{"Not enough buffer for const row iterator."}; + std::memcpy(begin, text.c_str(), std::size(text) + 1); + return begin + std::size(text); + } + static constexpr std::size_t + size_buffer(row::const_iterator const &) noexcept + { + return std::size(text) + 1; + } +}; + + +template<> struct string_traits +{ + static constexpr zview text{"[row::const_reverse_iterator]"}; + static pqxx::zview + to_buf(char *, char *, row::const_reverse_iterator const &) + { + return text; + } + static char * + into_buf(char *begin, char *end, row::const_reverse_iterator const &) + { + if ((end - begin) <= 30) + throw conversion_overrun{"Not enough buffer for const row iterator."}; + std::memcpy(begin, text.c_str(), std::size(text) + 1); + return begin + std::size(text); + } + static constexpr std::size_t + size_buffer(row::const_reverse_iterator const &) noexcept + { + return 100; + } +}; +} // namespace pqxx + +namespace +{ +void test_result_slicing() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + auto r{tx.exec("SELECT 1")}; + + PQXX_CHECK(not std::empty(r[0]), "A plain row shows up as empty."); + + // Empty slice at beginning of row. + pqxx::row s{r[0].slice(0, 0)}; + PQXX_CHECK(std::empty(s), "Empty slice does not show up as empty."); + PQXX_CHECK_EQUAL(std::size(s), 0, "Slicing produces wrong row size."); + PQXX_CHECK_EQUAL( + std::begin(s), std::end(s), "Slice begin()/end() are broken."); + PQXX_CHECK_EQUAL( + std::rbegin(s), std::rend(s), "Slice rbegin()/rend() are broken."); + + PQXX_CHECK_THROWS(s.at(0), pqxx::range_error, "at() does not throw."); + pqxx::row slice; + PQXX_CHECK_THROWS( + slice = r[0].slice(0, 2), pqxx::range_error, "No range check."); + pqxx::ignore_unused(slice); + PQXX_CHECK_THROWS( + slice = r[0].slice(1, 0), pqxx::range_error, "Can reverse-slice."); + pqxx::ignore_unused(slice); + + // Empty slice at end of row. + s = r[0].slice(1, 1); + PQXX_CHECK(std::empty(s), "empty() is broken."); + PQXX_CHECK_EQUAL(std::size(s), 0, "size() is broken."); + PQXX_CHECK_EQUAL(std::begin(s), std::end(s), "begin()/end() are broken."); + PQXX_CHECK_EQUAL( + std::rbegin(s), std::rend(s), "rbegin()/rend() are broken."); + + PQXX_CHECK_THROWS(s.at(0), pqxx::range_error, "at() is inconsistent."); + + // Slice that matches the entire row. + s = r[0].slice(0, 1); + PQXX_CHECK(not std::empty(s), "Nonempty slice shows up as empty."); + PQXX_CHECK_EQUAL(std::size(s), 1, "size() breaks for non-empty slice."); + PQXX_CHECK_EQUAL(std::begin(s) + 1, std::end(s), "Iteration is broken."); + PQXX_CHECK_EQUAL( + std::rbegin(s) + 1, std::rend(s), "Reverse iteration is broken."); + PQXX_CHECK_EQUAL(s.at(0).as(), 1, "Accessing a slice is broken."); + PQXX_CHECK_EQUAL(s[0].as(), 1, "operator[] is broken."); + PQXX_CHECK_THROWS(s.at(1).as(), pqxx::range_error, "at() is off."); + + // Meaningful slice at beginning of row. + r = tx.exec("SELECT 1, 2, 3"); + s = r[0].slice(0, 1); + PQXX_CHECK(not std::empty(s), "Slicing confuses empty()."); + PQXX_CHECK_THROWS( + s.at(1).as(), pqxx::range_error, "at() does not enforce slice."); + + // Meaningful slice that skips an initial column. + s = r[0].slice(1, 2); + PQXX_CHECK( + not std::empty(s), "Slicing away leading columns confuses empty()."); + PQXX_CHECK_EQUAL(s[0].as(), 2, "Slicing offset is broken."); + PQXX_CHECK_EQUAL( + std::begin(s)->as(), 2, "Iteration uses wrong offset."); + PQXX_CHECK_EQUAL( + std::begin(s) + 1, std::end(s), "Iteration has wrong range."); + PQXX_CHECK_EQUAL( + std::rbegin(s) + 1, std::rend(s), "Reverse iteration has wrong range."); + PQXX_CHECK_THROWS( + s.at(1).as(), pqxx::range_error, "Offset slicing is broken."); + + // Column names in a slice. + r = tx.exec("SELECT 1 AS one, 2 AS two, 3 AS three"); + s = r[0].slice(1, 2); + PQXX_CHECK_EQUAL(s["two"].as(), 2, "Column addressing breaks."); + PQXX_CHECK_THROWS( + pqxx::ignore_unused(s.column_number("one")), pqxx::argument_error, + "Can access column name before slice."); + PQXX_CHECK_THROWS( + pqxx::ignore_unused(s.column_number("three")), pqxx::argument_error, + "Can access column name after slice."); + PQXX_CHECK_EQUAL( + s.column_number("Two"), 0, "Column name is case sensitive."); + + // Identical column names. + r = tx.exec("SELECT 1 AS x, 2 AS x"); + s = r[0].slice(1, 2); + PQXX_CHECK_EQUAL(s["x"].as(), 2, "Identical column names break slice."); +} + + +PQXX_REGISTER_TEST(test_result_slicing); +} // namespace + +#include "pqxx/internal/ignore-deprecated-post.hxx" diff --git a/ext/libpqxx-7.7.3/test/unit/test_row.cxx b/ext/libpqxx-7.7.3/test/unit/test_row.cxx new file mode 100644 index 000000000..41770e3b1 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_row.cxx @@ -0,0 +1,84 @@ +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_row() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::result rows{tx.exec("SELECT 1, 2, 3")}; + pqxx::row r{rows[0]}; + PQXX_CHECK_EQUAL(std::size(r), 3, "Unexpected row size."); + PQXX_CHECK_EQUAL(r.at(0).as(), 1, "Wrong value at index 0."); + PQXX_CHECK(std::begin(r) != std::end(r), "Broken row iteration."); + PQXX_CHECK(std::begin(r) < std::end(r), "Row begin does not precede end."); + PQXX_CHECK(std::cbegin(r) == std::begin(r), "Wrong cbegin."); + PQXX_CHECK(std::cend(r) == std::end(r), "Wrong cend."); + PQXX_CHECK(std::rbegin(r) != std::rend(r), "Broken reverse row iteration."); + PQXX_CHECK(std::crbegin(r) == std::rbegin(r), "Wrong crbegin."); + PQXX_CHECK(std::crend(r) == std::rend(r), "Wrong crend."); + PQXX_CHECK_EQUAL(r.front().as(), 1, "Wrong row front()."); + PQXX_CHECK_EQUAL(r.back().as(), 3, "Wrong row back()."); +} + + +void test_row_iterator() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::result rows{tx.exec("SELECT 1, 2, 3")}; + + auto i{std::begin(rows[0])}; + PQXX_CHECK_EQUAL(i->as(), 1, "Row iterator is wrong."); + auto i2{i}; + PQXX_CHECK_EQUAL(i2->as(), 1, "Row iterator copy is wrong."); + i2++; + PQXX_CHECK_EQUAL(i2->as(), 2, "Row iterator increment is wrong."); + pqxx::row::const_iterator i3; + i3 = i2; + PQXX_CHECK_EQUAL(i3->as(), 2, "Row iterator assignment is wrong."); + + auto r{std::rbegin(rows[0])}; + PQXX_CHECK_EQUAL(r->as(), 3, "Row reverse iterator is wrong."); + auto r2{r}; + PQXX_CHECK_EQUAL(r2->as(), 3, "Row reverse iterator copy is wrong."); + r2++; + PQXX_CHECK_EQUAL( + r2->as(), 2, "Row reverse iterator increment is wrong."); + pqxx::row::const_reverse_iterator r3; + r3 = r2; + PQXX_CHECK_EQUAL( + i3->as(), 2, "Row reverse iterator assignment is wrong."); +} + + +void test_row_as() +{ + using pqxx::operator"" _zv; + + pqxx::connection conn; + pqxx::work tx{conn}; + + pqxx::row const r{tx.exec1("SELECT 1, 2, 3")}; + auto [one, two, three]{r.as()}; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + PQXX_CHECK_EQUAL(one, 1, "row::as() did not produce the right int."); + PQXX_CHECK_GREATER(two, 1.9, "row::as() did not produce the right float."); + PQXX_CHECK_LESS(two, 2.1, "row::as() did not produce the right float."); + PQXX_CHECK_EQUAL( + three, "3"_zv, "row::as() did not produce the right zview."); + + PQXX_CHECK_EQUAL( + std::get<0>(tx.exec1("SELECT 999").as()), 999, + "Unary tuple did not extract right."); +} + + +PQXX_REGISTER_TEST(test_row); +PQXX_REGISTER_TEST(test_row_iterator); +PQXX_REGISTER_TEST(test_row_as); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_separated_list.cxx b/ext/libpqxx-7.7.3/test/unit/test_separated_list.cxx new file mode 100644 index 000000000..748379b47 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_separated_list.cxx @@ -0,0 +1,33 @@ +#include + +#include "../test_helpers.hxx" + +// Test program for separated_list. + +namespace +{ +void test_separated_list() +{ + PQXX_CHECK_EQUAL( + pqxx::separated_list(",", std::vector{}), "", + "Empty list came out wrong."); + + PQXX_CHECK_EQUAL( + pqxx::separated_list(",", std::vector{5}), "5", + "Single-element list came out wrong."); + + PQXX_CHECK_EQUAL( + pqxx::separated_list(",", std::vector{3, 6}), "3,6", + "Things go wrong once separators come in."); + + std::vector const nums{1, 2, 3}; + PQXX_CHECK_EQUAL( + pqxx::separated_list( + "+", std::begin(nums), std::end(nums), + [](auto elt) { return *elt * 2; }), + "2+4+6", "Accessors don't seem to work."); +} + + +PQXX_REGISTER_TEST(test_separated_list); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_simultaneous_transactions.cxx b/ext/libpqxx-7.7.3/test/unit/test_simultaneous_transactions.cxx new file mode 100644 index 000000000..9c1e2fe9c --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_simultaneous_transactions.cxx @@ -0,0 +1,20 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_simultaneous_transactions() +{ + pqxx::connection conn; + + pqxx::nontransaction n1{conn}; + PQXX_CHECK_THROWS( + pqxx::nontransaction n2{conn}, std::logic_error, + "Allowed to open simultaneous nontransactions."); +} + + +PQXX_REGISTER_TEST(test_simultaneous_transactions); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_sql_cursor.cxx b/ext/libpqxx-7.7.3/test/unit/test_sql_cursor.cxx new file mode 100644 index 000000000..a0109a00b --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_sql_cursor.cxx @@ -0,0 +1,270 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_forward_sql_cursor() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + + // Plain owned, scoped, forward-only read-only cursor. + pqxx::internal::sql_cursor forward( + tx, "SELECT generate_series(1, 4)", "forward", + pqxx::cursor_base::forward_only, pqxx::cursor_base::read_only, + pqxx::cursor_base::owned, false); + + PQXX_CHECK_EQUAL(forward.pos(), 0, "Wrong initial position"); + PQXX_CHECK_EQUAL(forward.endpos(), -1, "Wrong initial endpos()"); + + auto empty_result{forward.empty_result()}; + PQXX_CHECK_EQUAL(std::size(empty_result), 0, "Empty result not empty"); + + auto displacement{0}; + auto one{forward.fetch(1, displacement)}; + PQXX_CHECK_EQUAL(std::size(one), 1, "Fetched wrong number of rows"); + PQXX_CHECK_EQUAL(one[0][0].as(), "1", "Unexpected result"); + PQXX_CHECK_EQUAL(displacement, 1, "Wrong displacement"); + PQXX_CHECK_EQUAL(forward.pos(), 1, "In wrong position"); + + auto offset{forward.move(1, displacement)}; + PQXX_CHECK_EQUAL(offset, 1, "Unexpected offset from move()"); + PQXX_CHECK_EQUAL(displacement, 1, "Unexpected displacement after move()"); + PQXX_CHECK_EQUAL(forward.pos(), 2, "Wrong position after move()"); + PQXX_CHECK_EQUAL(forward.endpos(), -1, "endpos() unexpectedly set"); + + auto row{forward.fetch(0, displacement)}; + PQXX_CHECK_EQUAL(std::size(row), 0, "fetch(0, displacement) returns rows"); + PQXX_CHECK_EQUAL(displacement, 0, "Unexpected displacement after fetch(0)"); + PQXX_CHECK_EQUAL(forward.pos(), 2, "fetch(0, displacement) affected pos()"); + + row = forward.fetch(0); + PQXX_CHECK_EQUAL(std::size(row), 0, "fetch(0) fetched wrong number of rows"); + PQXX_CHECK_EQUAL(forward.pos(), 2, "fetch(0) moved cursor"); + PQXX_CHECK_EQUAL(forward.pos(), 2, "fetch(0) affected pos()"); + + offset = forward.move(1); + PQXX_CHECK_EQUAL(offset, 1, "move(1) returned unexpected value"); + PQXX_CHECK_EQUAL(forward.pos(), 3, "move(1) after fetch(0) broke"); + + row = forward.fetch(1); + PQXX_CHECK_EQUAL( + std::size(row), 1, "fetch(1) returned wrong number of rows"); + PQXX_CHECK_EQUAL(forward.pos(), 4, "fetch(1) results in bad pos()"); + PQXX_CHECK_EQUAL(row[0][0].as(), "4", "pos() is lying"); + + empty_result = forward.fetch(1, displacement); + PQXX_CHECK_EQUAL(std::size(empty_result), 0, "Got rows at end of cursor"); + PQXX_CHECK_EQUAL(forward.pos(), 5, "Not at one-past-end position"); + PQXX_CHECK_EQUAL(forward.endpos(), 5, "Failed to notice end position"); + PQXX_CHECK_EQUAL(displacement, 1, "Wrong displacement at end position"); + + offset = forward.move(5, displacement); + PQXX_CHECK_EQUAL(offset, 0, "move() lied at end of result set"); + PQXX_CHECK_EQUAL(forward.pos(), 5, "pos() is beyond end"); + PQXX_CHECK_EQUAL(forward.endpos(), 5, "endpos() changed after end position"); + PQXX_CHECK_EQUAL(displacement, 0, "Wrong displacement after end position"); + + // Move through entire result set at once. + pqxx::internal::sql_cursor forward2( + tx, "SELECT generate_series(1, 4)", "forward", + pqxx::cursor_base::forward_only, pqxx::cursor_base::read_only, + pqxx::cursor_base::owned, false); + + // Move through entire result set at once. + offset = forward2.move(pqxx::cursor_base::all(), displacement); + PQXX_CHECK_EQUAL(offset, 4, "Unexpected number of rows in result set"); + PQXX_CHECK_EQUAL(displacement, 5, "displacement != rows+1"); + PQXX_CHECK_EQUAL(forward2.pos(), 5, "Bad pos() after skipping all rows"); + PQXX_CHECK_EQUAL(forward2.endpos(), 5, "Bad endpos() after skipping"); + + pqxx::internal::sql_cursor forward3( + tx, "SELECT generate_series(1, 4)", "forward", + pqxx::cursor_base::forward_only, pqxx::cursor_base::read_only, + pqxx::cursor_base::owned, false); + + // Fetch entire result set at once. + auto rows{forward3.fetch(pqxx::cursor_base::all(), displacement)}; + PQXX_CHECK_EQUAL( + std::size(rows), 4, "Unexpected number of rows in result set"); + PQXX_CHECK_EQUAL(displacement, 5, "displacement != rows+1"); + PQXX_CHECK_EQUAL(forward3.pos(), 5, "Bad pos() after fetching all rows"); + PQXX_CHECK_EQUAL(forward3.endpos(), 5, "Bad endpos() after fetching"); + + pqxx::internal::sql_cursor forward_empty( + tx, "SELECT generate_series(0, -1)", "forward_empty", + pqxx::cursor_base::forward_only, pqxx::cursor_base::read_only, + pqxx::cursor_base::owned, false); + + offset = forward_empty.move(3, displacement); + PQXX_CHECK_EQUAL(forward_empty.pos(), 1, "Bad pos() at end of result"); + PQXX_CHECK_EQUAL(forward_empty.endpos(), 1, "Bad endpos() in empty result"); + PQXX_CHECK_EQUAL(displacement, 1, "Bad displacement in empty result"); + PQXX_CHECK_EQUAL(offset, 0, "move() in empty result counted rows"); +} + +void test_scroll_sql_cursor() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + pqxx::internal::sql_cursor scroll( + tx, "SELECT generate_series(1, 10)", "scroll", + pqxx::cursor_base::random_access, pqxx::cursor_base::read_only, + pqxx::cursor_base::owned, false); + + PQXX_CHECK_EQUAL(scroll.pos(), 0, "Scroll cursor's initial pos() is wrong"); + PQXX_CHECK_EQUAL(scroll.endpos(), -1, "New scroll cursor has endpos() set"); + + auto rows{scroll.fetch(pqxx::cursor_base::next())}; + PQXX_CHECK_EQUAL(std::size(rows), 1, "Scroll cursor is broken"); + PQXX_CHECK_EQUAL(scroll.pos(), 1, "Scroll cursor's pos() is broken"); + PQXX_CHECK_EQUAL(scroll.endpos(), -1, "endpos() set prematurely"); + + // Turn cursor around. This is where we begin to feel SQL cursors' + // semantics: we pre-decrement, ending up on the position in front of the + // first row and returning no rows. + rows = scroll.fetch(pqxx::cursor_base::prior()); + PQXX_CHECK_EQUAL(std::empty(rows), true, "Turning around on fetch() broke"); + PQXX_CHECK_EQUAL(scroll.pos(), 0, "pos() is not back at zero"); + PQXX_CHECK_EQUAL( + scroll.endpos(), -1, "endpos() set on wrong side of result"); + + // Bounce off the left-hand side of the result set. Can't move before the + // starting position. + auto offset{0}, displacement{0}; + offset = scroll.move(-3, displacement); + PQXX_CHECK_EQUAL(offset, 0, "Rows found before beginning"); + PQXX_CHECK_EQUAL(displacement, 0, "Failed to bounce off beginning"); + PQXX_CHECK_EQUAL(scroll.pos(), 0, "pos() moved back from zero"); + PQXX_CHECK_EQUAL(scroll.endpos(), -1, "endpos() set on left-side bounce"); + + // Try bouncing off the left-hand side a little harder. Take 4 paces away + // from the boundary and run into it. + offset = scroll.move(4, displacement); + PQXX_CHECK_EQUAL(offset, 4, "Offset mismatch"); + PQXX_CHECK_EQUAL(displacement, 4, "Displacement mismatch"); + PQXX_CHECK_EQUAL(scroll.pos(), 4, "Position mismatch"); + PQXX_CHECK_EQUAL(scroll.endpos(), -1, "endpos() set at weird time"); + + offset = scroll.move(-10, displacement); + PQXX_CHECK_EQUAL(offset, 3, "Offset mismatch"); + PQXX_CHECK_EQUAL(displacement, -4, "Displacement mismatch"); + PQXX_CHECK_EQUAL(scroll.pos(), 0, "Hard bounce failed"); + PQXX_CHECK_EQUAL(scroll.endpos(), -1, "endpos() set during hard bounce"); + + rows = scroll.fetch(3); + PQXX_CHECK_EQUAL(scroll.pos(), 3, "Bad pos()"); + PQXX_CHECK_EQUAL(std::size(rows), 3, "Wrong number of rows"); + PQXX_CHECK_EQUAL(rows[2][0].as(), 3, "pos() does not match data"); + rows = scroll.fetch(-1); + PQXX_CHECK_EQUAL(scroll.pos(), 2, "Bad pos()"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 2, "pos() does not match data"); + + rows = scroll.fetch(1); + PQXX_CHECK_EQUAL(scroll.pos(), 3, "Bad pos() after inverse turnaround"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 3, "Data position mismatch"); +} + + +void test_adopted_sql_cursor() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + + tx.exec0( + "DECLARE adopted SCROLL CURSOR FOR " + "SELECT generate_series(1, 3)"); + pqxx::internal::sql_cursor adopted(tx, "adopted", pqxx::cursor_base::owned); + PQXX_CHECK_EQUAL(adopted.pos(), -1, "Adopted cursor has known pos()"); + PQXX_CHECK_EQUAL(adopted.endpos(), -1, "Adopted cursor has known endpos()"); + + auto displacement{0}; + auto rows{adopted.fetch(pqxx::cursor_base::all(), displacement)}; + PQXX_CHECK_EQUAL(std::size(rows), 3, "Wrong number of rows in result"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 1, "Wrong result data"); + PQXX_CHECK_EQUAL(rows[2][0].as(), 3, "Wrong result data"); + PQXX_CHECK_EQUAL(displacement, 4, "Wrong displacement"); + PQXX_CHECK_EQUAL( + adopted.pos(), -1, "End-of-result set pos() on adopted cur"); + PQXX_CHECK_EQUAL(adopted.endpos(), -1, "endpos() set too early"); + + rows = adopted.fetch(pqxx::cursor_base::backward_all(), displacement); + PQXX_CHECK_EQUAL(std::size(rows), 3, "Wrong number of rows in result"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 3, "Wrong result data"); + PQXX_CHECK_EQUAL(rows[2][0].as(), 1, "Wrong result data"); + PQXX_CHECK_EQUAL(displacement, -4, "Wrong displacement"); + PQXX_CHECK_EQUAL(adopted.pos(), 0, "Failed to recognize starting position"); + PQXX_CHECK_EQUAL(adopted.endpos(), -1, "endpos() set too early"); + + auto offset{adopted.move(pqxx::cursor_base::all())}; + PQXX_CHECK_EQUAL(offset, 3, "Unexpected move() offset"); + PQXX_CHECK_EQUAL(adopted.pos(), 4, "Bad position on adopted cursor"); + PQXX_CHECK_EQUAL(adopted.endpos(), 4, "endpos() not set properly"); + + // Owned adopted cursors are cleaned up on destruction. + pqxx::connection conn2; + pqxx::work tx2(conn2, "tx2"); + tx2.exec0( + "DECLARE adopted2 CURSOR FOR " + "SELECT generate_series(1, 3)"); + { + pqxx::internal::sql_cursor(tx2, "adopted2", pqxx::cursor_base::owned); + } + // Modern backends: accessing the cursor now is an error, as you'd expect. + PQXX_CHECK_THROWS( + tx2.exec("FETCH 1 IN adopted2"), pqxx::sql_error, + "Owned adopted cursor not cleaned up"); + + tx2.abort(); + + pqxx::work tx3(conn2, "tx3"); + tx3.exec( + "DECLARE adopted3 CURSOR FOR " + "SELECT generate_series(1, 3)"); + { + pqxx::internal::sql_cursor(tx3, "adopted3", pqxx::cursor_base::loose); + } + tx3.exec("MOVE 1 IN adopted3"); +} + +void test_hold_cursor() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + + // "With hold" cursor is kept after commit. + pqxx::internal::sql_cursor with_hold( + tx, "SELECT generate_series(1, 3)", "hold_cursor", + pqxx::cursor_base::forward_only, pqxx::cursor_base::read_only, + pqxx::cursor_base::owned, true); + tx.commit(); + pqxx::work tx2(conn, "tx2"); + auto rows{with_hold.fetch(1)}; + PQXX_CHECK_EQUAL( + std::size(rows), 1, "Did not get 1 row from with-hold cursor"); + + // Cursor without hold is closed on commit. + pqxx::internal::sql_cursor no_hold( + tx2, "SELECT generate_series(1, 3)", "no_hold_cursor", + pqxx::cursor_base::forward_only, pqxx::cursor_base::read_only, + pqxx::cursor_base::owned, false); + tx2.commit(); + pqxx::work tx3(conn, "tx3"); + PQXX_CHECK_THROWS( + no_hold.fetch(1), pqxx::sql_error, "Cursor not closed on commit"); +} + + +void cursor_tests() +{ + test_forward_sql_cursor(); + test_scroll_sql_cursor(); + test_adopted_sql_cursor(); + test_hold_cursor(); +} + + +PQXX_REGISTER_TEST(cursor_tests); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_stateless_cursor.cxx b/ext/libpqxx-7.7.3/test/unit/test_stateless_cursor.cxx new file mode 100644 index 000000000..79307c19e --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_stateless_cursor.cxx @@ -0,0 +1,85 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_stateless_cursor() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + + pqxx::stateless_cursor< + pqxx::cursor_base::read_only, pqxx::cursor_base::owned> + empty(tx, "SELECT generate_series(0, -1)", "empty", false); + + auto rows{empty.retrieve(0, 0)}; + PQXX_CHECK_EQUAL(std::empty(rows), true, "Empty result not empty"); + rows = empty.retrieve(0, 1); + PQXX_CHECK_EQUAL(std::size(rows), 0, "Empty result returned rows"); + + PQXX_CHECK_EQUAL(empty.size(), 0, "Empty cursor not empty"); + + PQXX_CHECK_THROWS( + empty.retrieve(1, 0), std::out_of_range, "Empty cursor tries to retrieve"); + + pqxx::stateless_cursor< + pqxx::cursor_base::read_only, pqxx::cursor_base::owned> + stateless(tx, "SELECT generate_series(0, 9)", "stateless", false); + + PQXX_CHECK_EQUAL(stateless.size(), 10, "stateless_cursor::size() mismatch"); + + // Retrieve nothing. + rows = stateless.retrieve(1, 1); + PQXX_CHECK_EQUAL(std::size(rows), 0, "1-to-1 retrieval not empty"); + + // Retrieve two rows. + rows = stateless.retrieve(1, 3); + PQXX_CHECK_EQUAL(std::size(rows), 2, "Retrieved wrong number of rows"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 1, "Data/position mismatch"); + PQXX_CHECK_EQUAL(rows[1][0].as(), 2, "Data/position mismatch"); + + // Retrieve same rows in reverse. + rows = stateless.retrieve(2, 0); + PQXX_CHECK_EQUAL(std::size(rows), 2, "Retrieved wrong number of rows"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 2, "Data/position mismatch"); + PQXX_CHECK_EQUAL(rows[1][0].as(), 1, "Data/position mismatch"); + + // Retrieve beyond end. + rows = stateless.retrieve(9, 13); + PQXX_CHECK_EQUAL(std::size(rows), 1, "Row count wrong at end"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 9, "Data/pos mismatch at end"); + + // Retrieve beyond beginning. + rows = stateless.retrieve(0, -4); + PQXX_CHECK_EQUAL(std::size(rows), 1, "Row count wrong at beginning"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 0, "Data/pos mismatch at beginning"); + + // Retrieve entire result set backwards. + rows = stateless.retrieve(10, -15); + PQXX_CHECK_EQUAL( + std::size(rows), 10, "Reverse complete retrieval is broken"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 9, "Data mismatch"); + PQXX_CHECK_EQUAL(rows[9][0].as(), 0, "Data mismatch"); + + // Normal usage pattern: step through result set, 4 rows at a time. + rows = stateless.retrieve(0, 4); + PQXX_CHECK_EQUAL(std::size(rows), 4, "Wrong batch size"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 0, "Batch in wrong place"); + PQXX_CHECK_EQUAL(rows[3][0].as(), 3, "Batch in wrong place"); + + rows = stateless.retrieve(4, 8); + PQXX_CHECK_EQUAL(std::size(rows), 4, "Wrong batch size"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 4, "Batch in wrong place"); + PQXX_CHECK_EQUAL(rows[3][0].as(), 7, "Batch in wrong place"); + + rows = stateless.retrieve(8, 12); + PQXX_CHECK_EQUAL(std::size(rows), 2, "Wrong batch size"); + PQXX_CHECK_EQUAL(rows[0][0].as(), 8, "Batch in wrong place"); + PQXX_CHECK_EQUAL(rows[1][0].as(), 9, "Batch in wrong place"); +} + + +PQXX_REGISTER_TEST(test_stateless_cursor); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_strconv.cxx b/ext/libpqxx-7.7.3/test/unit/test_strconv.cxx new file mode 100644 index 000000000..602084e18 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_strconv.cxx @@ -0,0 +1,143 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +enum colour +{ + red, + green, + blue +}; +enum class weather : short +{ + hot, + cold, + wet +}; +enum class many : unsigned long long +{ + bottom = 0, + top = std::numeric_limits::max(), +}; +} // namespace + +namespace pqxx +{ +PQXX_DECLARE_ENUM_CONVERSION(colour); +PQXX_DECLARE_ENUM_CONVERSION(weather); +PQXX_DECLARE_ENUM_CONVERSION(many); +} // namespace pqxx + + +namespace +{ +void test_strconv_bool() +{ + PQXX_CHECK_EQUAL(pqxx::to_string(false), "false", "Wrong to_string(false)."); + PQXX_CHECK_EQUAL(pqxx::to_string(true), "true", "Wrong to_string(true)."); + + bool result; + pqxx::from_string("false", result); + PQXX_CHECK_EQUAL(result, false, "Wrong from_string('false')."); + pqxx::from_string("FALSE", result); + PQXX_CHECK_EQUAL(result, false, "Wrong from_string('FALSE')."); + pqxx::from_string("f", result); + PQXX_CHECK_EQUAL(result, false, "Wrong from_string('f')."); + pqxx::from_string("F", result); + PQXX_CHECK_EQUAL(result, false, "Wrong from_string('F')."); + pqxx::from_string("0", result); + PQXX_CHECK_EQUAL(result, false, "Wrong from_string('0')."); + pqxx::from_string("true", result); + PQXX_CHECK_EQUAL(result, true, "Wrong from_string('true')."); + pqxx::from_string("TRUE", result); + PQXX_CHECK_EQUAL(result, true, "Wrong from_string('TRUE')."); + pqxx::from_string("t", result); + PQXX_CHECK_EQUAL(result, true, "Wrong from_string('t')."); + pqxx::from_string("T", result); + PQXX_CHECK_EQUAL(result, true, "Wrong from_string('T')."); + pqxx::from_string("1", result); + PQXX_CHECK_EQUAL(result, true, "Wrong from_string('1')."); +} + + +void test_strconv_enum() +{ + PQXX_CHECK_EQUAL(pqxx::to_string(red), "0", "Enum value did not convert."); + PQXX_CHECK_EQUAL(pqxx::to_string(green), "1", "Enum value did not convert."); + PQXX_CHECK_EQUAL(pqxx::to_string(blue), "2", "Enum value did not convert."); + + colour col; + pqxx::from_string("2", col); + PQXX_CHECK_EQUAL(col, blue, "Could not recover enum value from string."); +} + + +void test_strconv_class_enum() +{ + PQXX_CHECK_EQUAL( + pqxx::to_string(weather::hot), "0", "Class enum value did not convert."); + PQXX_CHECK_EQUAL( + pqxx::to_string(weather::wet), "2", "Enum value did not convert."); + + weather w; + pqxx::from_string("2", w); + PQXX_CHECK_EQUAL( + w, weather::wet, "Could not recover class enum value from string."); + + PQXX_CHECK_EQUAL( + pqxx::to_string(many::bottom), "0", + "Small wide enum did not convert right."); + PQXX_CHECK_EQUAL( + pqxx::to_string(many::top), + pqxx::to_string(std::numeric_limits::max()), + "Large wide enum did not convert right."); +} + + +void test_strconv_optional() +{ + PQXX_CHECK_THROWS( + pqxx::to_string(std::optional{}), pqxx::conversion_error, + "Converting an empty optional did not throw conversion error."); + PQXX_CHECK_EQUAL( + pqxx::to_string(std::optional{std::in_place, 10}), "10", + "std::optional does not convert right."); + PQXX_CHECK_EQUAL( + pqxx::to_string(std::optional{std::in_place, -10000}), "-10000", + "std::optional does not convert right."); +} + + +void test_strconv_smart_pointer() +{ + PQXX_CHECK_THROWS( + pqxx::to_string(std::unique_ptr{}), pqxx::conversion_error, + "Converting an empty unique_ptr did not throw conversion error."); + PQXX_CHECK_EQUAL( + pqxx::to_string(std::make_unique(10)), "10", + "std::unique_ptr does not convert right."); + PQXX_CHECK_EQUAL( + pqxx::to_string(std::make_unique(-10000)), "-10000", + "std::unique_ptr does not convert right."); + + PQXX_CHECK_THROWS( + pqxx::to_string(std::shared_ptr{}), pqxx::conversion_error, + "Converting an empty shared_ptr did not throw conversion error."); + PQXX_CHECK_EQUAL( + pqxx::to_string(std::make_shared(10)), "10", + "std::shared_ptr does not convert right."); + PQXX_CHECK_EQUAL( + pqxx::to_string(std::make_shared(-10000)), "-10000", + "std::shared_ptr does not convert right."); +} + + +PQXX_REGISTER_TEST(test_strconv_bool); +PQXX_REGISTER_TEST(test_strconv_enum); +PQXX_REGISTER_TEST(test_strconv_class_enum); +PQXX_REGISTER_TEST(test_strconv_optional); +PQXX_REGISTER_TEST(test_strconv_smart_pointer); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_stream_from.cxx b/ext/libpqxx-7.7.3/test/unit/test_stream_from.cxx new file mode 100644 index 000000000..d8adb8bd1 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_stream_from.cxx @@ -0,0 +1,344 @@ +#include +#include + +#include "../test_helpers.hxx" +#include "../test_types.hxx" + +#include +#include +#include +#include +#include +#include + +#include + + +namespace +{ +void test_nonoptionals(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto extractor{pqxx::stream_from::query( + tx, "SELECT * FROM stream_from_test ORDER BY number0")}; + PQXX_CHECK(extractor, "stream_from failed to initialize."); + + std::tuple got_tuple; + + try + { + // We can't read the "910" row -- it contains nulls, which our tuple does + // not accept. + extractor >> got_tuple; + PQXX_CHECK_NOTREACHED( + "Failed to fail to stream null values into null-less fields."); + } + catch (pqxx::conversion_error const &e) + { + std::string const what{e.what()}; + if (what.find("null") == std::string::npos) + throw; + pqxx::test::expected_exception( + "Could not stream nulls into null-less fields: " + what); + } + + // The stream is still good though. + // The second tuple is fine. + extractor >> got_tuple; + PQXX_CHECK(extractor, "Stream ended prematurely."); + + PQXX_CHECK_EQUAL(std::get<0>(got_tuple), 1234, "Bad value."); + // Don't know much about the timestamp, but let's assume it starts with a + // year in the second millennium. + PQXX_CHECK( + std::get<1>(got_tuple).at(0) == '2', "Bad value. Expected timestamp."); + PQXX_CHECK_LESS( + std::size(std::get<1>(got_tuple)), 40u, "Unexpected length."); + PQXX_CHECK_GREATER( + std::size(std::get<1>(got_tuple)), 20u, "Unexpected length."); + PQXX_CHECK_EQUAL(std::get<2>(got_tuple), 4321, "Bad value."); + PQXX_CHECK_EQUAL(std::get<3>(got_tuple), (ipv4{8, 8, 8, 8}), "Bad value."); + PQXX_CHECK_EQUAL(std::get<4>(got_tuple), "hello\n \tworld", "Bad value."); + PQXX_CHECK_EQUAL( + std::get<5>(got_tuple), (bytea{'\x00', '\x01', '\x02'}), "Bad value."); + + // The third tuple contains some nulls. For what it's worth, when we *know* + // that we're getting nulls, we can stream them into nullptr_t fields. + std::tuple< + int, std::string, std::nullptr_t, std::nullptr_t, std::string, bytea> + tup_w_nulls; + + extractor >> tup_w_nulls; + PQXX_CHECK(extractor, "Stream ended prematurely."); + + PQXX_CHECK_EQUAL(std::get<0>(tup_w_nulls), 5678, "Bad value."); + PQXX_CHECK(std::get<2>(tup_w_nulls) == nullptr, "Bad null."); + PQXX_CHECK(std::get<3>(tup_w_nulls) == nullptr, "Bad null."); + + // We're at the end of the stream. + extractor >> tup_w_nulls; + PQXX_CHECK(not extractor, "Stream did not end."); + + // Of course we can't stream a non-null value into a nullptr field. + auto ex2{pqxx::stream_from::query(tx, "SELECT 1")}; + std::tuple null_tup; + try + { + ex2 >> null_tup; + PQXX_CHECK_NOTREACHED( + "stream_from should have refused to convert non-null value to " + "nullptr_t."); + } + catch (pqxx::conversion_error const &e) + { + std::string const what{e.what()}; + if (what.find("null") == std::string::npos) + throw; + pqxx::test::expected_exception( + std::string{"Could not extract row: "} + what); + } + ex2 >> null_tup; + PQXX_CHECK(not ex2, "Stream did not end."); + + PQXX_CHECK_SUCCEEDS( + tx.exec1("SELECT 1"), "Could not use transaction after stream_from."); +} + + +void test_bad_tuples(pqxx::connection &conn) +{ + pqxx::work tx{conn}; + auto extractor{pqxx::stream_from::table(tx, {"stream_from_test"})}; + PQXX_CHECK(extractor, "stream_from failed to initialize"); + + std::tuple got_tuple_too_short; + try + { + extractor >> got_tuple_too_short; + PQXX_CHECK_NOTREACHED("stream_from improperly read first row"); + } + catch (pqxx::usage_error const &e) + { + std::string what{e.what()}; + if ( + what.find("1") == std::string::npos or + what.find("6") == std::string::npos) + throw; + pqxx::test::expected_exception("Tuple is wrong size: " + what); + } + + std::tuple + got_tuple_too_long; + try + { + extractor >> got_tuple_too_long; + PQXX_CHECK_NOTREACHED("stream_from improperly read first row"); + } + catch (pqxx::usage_error const &e) + { + std::string what{e.what()}; + if ( + what.find("6") == std::string::npos or + what.find("7") == std::string::npos) + throw; + pqxx::test::expected_exception("Could not extract row: " + what); + } + + extractor.complete(); +} + + +#define ASSERT_FIELD_EQUAL(OPT, VAL) \ + PQXX_CHECK(static_cast(OPT), "unexpected null field"); \ + PQXX_CHECK_EQUAL(*OPT, VAL, "field value mismatch") +#define ASSERT_FIELD_NULL(OPT) \ + PQXX_CHECK(not static_cast(OPT), "expected null field") + + +template class O> +void test_optional(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto extractor{pqxx::stream_from::query( + tx, "SELECT * FROM stream_from_test ORDER BY number0")}; + PQXX_CHECK(extractor, "stream_from failed to initialize"); + + std::tuple, O, O, O, O> + got_tuple; + + extractor >> got_tuple; + PQXX_CHECK(extractor, "stream_from failed to read third row"); + PQXX_CHECK_EQUAL(std::get<0>(got_tuple), 910, "field value mismatch"); + ASSERT_FIELD_NULL(std::get<1>(got_tuple)); + ASSERT_FIELD_NULL(std::get<2>(got_tuple)); + ASSERT_FIELD_NULL(std::get<3>(got_tuple)); + ASSERT_FIELD_EQUAL(std::get<4>(got_tuple), "\\N"); + ASSERT_FIELD_EQUAL(std::get<5>(got_tuple), bytea{}); + + extractor >> got_tuple; + PQXX_CHECK(extractor, "stream_from failed to read first row."); + PQXX_CHECK_EQUAL(std::get<0>(got_tuple), 1234, "Field value mismatch."); + PQXX_CHECK( + static_cast(std::get<1>(got_tuple)), "Unexpected null field."); + // PQXX_CHECK_EQUAL(*std::get<1>(got_tuple), , "field value mismatch"); + ASSERT_FIELD_EQUAL(std::get<2>(got_tuple), 4321); + ASSERT_FIELD_EQUAL(std::get<3>(got_tuple), (ipv4{8, 8, 8, 8})); + ASSERT_FIELD_EQUAL(std::get<4>(got_tuple), "hello\n \tworld"); + ASSERT_FIELD_EQUAL(std::get<5>(got_tuple), (bytea{'\x00', '\x01', '\x02'})); + + extractor >> got_tuple; + PQXX_CHECK(extractor, "stream_from failed to read second row"); + PQXX_CHECK_EQUAL(std::get<0>(got_tuple), 5678, "field value mismatch"); + ASSERT_FIELD_EQUAL(std::get<1>(got_tuple), "2018-11-17 21:23:00"); + ASSERT_FIELD_NULL(std::get<2>(got_tuple)); + ASSERT_FIELD_NULL(std::get<3>(got_tuple)); + ASSERT_FIELD_EQUAL(std::get<4>(got_tuple), "\u3053\u3093\u306b\u3061\u308f"); + ASSERT_FIELD_EQUAL( + std::get<5>(got_tuple), (bytea{'f', 'o', 'o', ' ', 'b', 'a', 'r', '\0'})); + + extractor >> got_tuple; + PQXX_CHECK(not extractor, "stream_from failed to detect end of stream"); + + extractor.complete(); +} + + +void test_stream_from() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + tx.exec0( + "CREATE TEMP TABLE stream_from_test (" + "number0 INT NOT NULL," + "ts1 TIMESTAMP NULL," + "number2 INT NULL," + "addr3 INET NULL," + "txt4 TEXT NULL," + "bin5 BYTEA NOT NULL" + ")"); + tx.exec_params( + "INSERT INTO stream_from_test VALUES ($1,$2,$3,$4,$5,$6)", 910, nullptr, + nullptr, nullptr, "\\N", bytea{}); + tx.exec_params( + "INSERT INTO stream_from_test VALUES ($1,$2,$3,$4,$5,$6)", 1234, "now", + 4321, ipv4{8, 8, 8, 8}, "hello\n \tworld", bytea{'\x00', '\x01', '\x02'}); + tx.exec_params( + "INSERT INTO stream_from_test VALUES ($1,$2,$3,$4,$5,$6)", 5678, + "2018-11-17 21:23:00", nullptr, nullptr, "\u3053\u3093\u306b\u3061\u308f", + bytea{'f', 'o', 'o', ' ', 'b', 'a', 'r', '\0'}); + tx.commit(); + + test_nonoptionals(conn); + test_bad_tuples(conn); + std::cout << "testing `std::unique_ptr` as optional...\n"; + test_optional(conn); + std::cout << "testing `std::optional` as optional...\n"; + test_optional(conn); +} + + +void test_stream_from_does_escaping() +{ + std::string const input{"a\t\n\n\n \\b\nc"}; + pqxx::connection conn; + pqxx::work tx{conn}; + tx.exec0("CREATE TEMP TABLE badstr (str text)"); + tx.exec0("INSERT INTO badstr (str) VALUES (" + tx.quote(input) + ")"); + auto reader{pqxx::stream_from::table(tx, {"badstr"})}; + std::tuple out; + reader >> out; + PQXX_CHECK_EQUAL( + std::get<0>(out), input, "stream_from got weird characters wrong."); +} + + +void test_stream_from_does_iteration() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + tx.exec0("CREATE TEMP TABLE str (s text)"); + tx.exec0("INSERT INTO str (s) VALUES ('foo')"); + auto reader{pqxx::stream_from::table(tx, {"str"})}; + + int i{0}; + std::string out; + for (std::tuple t : reader.iter()) + { + i++; + out = std::get<0>(t); + } + PQXX_CHECK_EQUAL(i, 1, "Wrong number of iterations."); + PQXX_CHECK_EQUAL(out, "foo", "Got wrong string."); + + tx.exec0("INSERT INTO str (s) VALUES ('bar')"); + i = 0; + std::set strings; + auto reader2{pqxx::stream_from::table(tx, {"str"})}; + for (std::tuple t : reader2.iter()) + { + i++; + strings.insert(std::get<0>(t)); + } + PQXX_CHECK_EQUAL(i, 2, "Wrong number of iterations."); + PQXX_CHECK_EQUAL( + std::size(strings), 2u, "Wrong number of strings retrieved."); + PQXX_CHECK(strings.find("foo") != std::end(strings), "Missing key."); + PQXX_CHECK(strings.find("bar") != std::end(strings), "Missing key."); +} + + +void test_transaction_stream_from() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + tx.exec0("CREATE TEMP TABLE sample (id integer, name varchar)"); + tx.exec0("INSERT INTO sample (id, name) VALUES (321, 'something')"); + + int items{0}; + int id{0}; + std::string name; + + for (auto [iid, iname] : + tx.stream("SELECT id, name FROM sample")) + { + items++; + id = iid; + name = iname; + } + PQXX_CHECK_EQUAL(items, 1, "Wrong number of iterations."); + PQXX_CHECK_EQUAL(id, 321, "Got wrong int."); + PQXX_CHECK_EQUAL(name, std::string{"something"}, "Got wrong string."); + + PQXX_CHECK_EQUAL( + tx.query_value("SELECT 4"), 4, + "Loop did not relinquish transaction."); +} + + +void test_stream_from_read_row() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + tx.exec0("CREATE TEMP TABLE sample (id integer, name varchar, opt integer)"); + tx.exec0("INSERT INTO sample (id, name) VALUES (321, 'something')"); + + auto stream{pqxx::stream_from::table(tx, {"sample"})}; + auto fields{stream.read_row()}; + PQXX_CHECK_EQUAL(fields->size(), 3ul, "Wrong number of fields."); + PQXX_CHECK_EQUAL( + std::string((*fields)[0]), "321", "Integer field came out wrong."); + PQXX_CHECK_EQUAL( + std::string((*fields)[1]), "something", "Text field came out wrong."); + PQXX_CHECK(std::data((*fields)[2]) == nullptr, "Null field came out wrong."); + + auto last{stream.read_row()}; + PQXX_CHECK(last == nullptr, "No null pointer at end of stream."); +} + + +PQXX_REGISTER_TEST(test_stream_from); +PQXX_REGISTER_TEST(test_stream_from_does_escaping); +PQXX_REGISTER_TEST(test_stream_from_does_iteration); +PQXX_REGISTER_TEST(test_transaction_stream_from); +PQXX_REGISTER_TEST(test_stream_from_read_row); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_stream_to.cxx b/ext/libpqxx-7.7.3/test/unit/test_stream_to.cxx new file mode 100644 index 000000000..ae4eb3c65 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_stream_to.cxx @@ -0,0 +1,445 @@ +#include +#include + +#include +#include + +#include "../test_helpers.hxx" +#include "../test_types.hxx" + +namespace +{ +std::string truncate_sql_error(std::string const &what) +{ + auto trunc{what.substr(0, what.find('\n'))}; + if (std::size(trunc) > 64) + trunc = trunc.substr(0, 61) + "..."; + return trunc; +} + + +void test_nonoptionals(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; + PQXX_CHECK(inserter, "stream_to failed to initialize"); + + auto const nonascii{"\u3053\u3093\u306b\u3061\u308f"}; + bytea const binary{'\x00', '\x01', '\x02'}, + text{'f', 'o', 'o', ' ', 'b', 'a', 'r', '\0'}; + + inserter << std::make_tuple( + 1234, "now", 4321, ipv4{8, 8, 4, 4}, "hello nonoptional world", binary); + inserter << std::make_tuple( + 5678, "2018-11-17 21:23:00", nullptr, nullptr, nonascii, text); + inserter << std::make_tuple(910, nullptr, nullptr, nullptr, "\\N", bytea{}); + + inserter.complete(); + + auto r1{tx.exec1("SELECT * FROM stream_to_test WHERE number0 = 1234")}; + PQXX_CHECK_EQUAL(r1[0].as(), 1234, "Read back wrong first int."); + PQXX_CHECK_EQUAL( + r1[4].as(), "hello nonoptional world", + "Read back wrong string."); + PQXX_CHECK_EQUAL(r1[3].as(), ipv4(8, 8, 4, 4), "Read back wrong ip."); + PQXX_CHECK_EQUAL(r1[5].as(), binary, "Read back wrong bytea."); + + auto r2{tx.exec1("SELECT * FROM stream_to_test WHERE number0 = 5678")}; + PQXX_CHECK_EQUAL(r2[0].as(), 5678, "Wrong int on second row."); + PQXX_CHECK(r2[2].is_null(), "Field 2 was meant to be null."); + PQXX_CHECK(r2[3].is_null(), "Field 3 was meant to be null."); + PQXX_CHECK_EQUAL(r2[4].as(), nonascii, "Wrong non-ascii text."); + tx.commit(); +} + +void test_nonoptionals_fold(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; + PQXX_CHECK(inserter, "stream_to failed to initialize"); + + auto const nonascii{"\u3053\u3093\u306b\u3061\u308f"}; + bytea const binary{'\x00', '\x01', '\x02'}, + text{'f', 'o', 'o', ' ', 'b', 'a', 'r', '\0'}; + + inserter.write_values( + 1234, "now", 4321, ipv4{8, 8, 4, 4}, "hello nonoptional world", binary); + inserter.write_values( + 5678, "2018-11-17 21:23:00", nullptr, nullptr, nonascii, text); + inserter.write_values(910, nullptr, nullptr, nullptr, "\\N", bytea{}); + + inserter.complete(); + + auto r1{tx.exec1("SELECT * FROM stream_to_test WHERE number0 = 1234")}; + PQXX_CHECK_EQUAL(r1[0].as(), 1234, "Read back wrong first int."); + PQXX_CHECK_EQUAL( + r1[4].as(), "hello nonoptional world", + "Read back wrong string."); + PQXX_CHECK_EQUAL(r1[3].as(), ipv4(8, 8, 4, 4), "Read back wrong ip."); + PQXX_CHECK_EQUAL(r1[5].as(), binary, "Read back wrong bytera."); + + auto r2{tx.exec1("SELECT * FROM stream_to_test WHERE number0 = 5678")}; + PQXX_CHECK_EQUAL(r2[0].as(), 5678, "Wrong int on second row."); + PQXX_CHECK(r2[2].is_null(), "Field 2 was meant to be null."); + PQXX_CHECK(r2[3].is_null(), "Field 3 was meant to be null."); + PQXX_CHECK_EQUAL(r2[4].as(), nonascii, "Wrong non-ascii text."); + tx.commit(); +} + + +/// Try to violate stream_to_test's not-null constraint using a stream_to. +void insert_bad_null_tuple(pqxx::stream_to &inserter) +{ + inserter << std::make_tuple( + nullptr, "now", 4321, ipv4{8, 8, 8, 8}, "hello world", + bytea{'\x00', '\x01', '\x02'}); + inserter.complete(); +} + + +void test_bad_null(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; + PQXX_CHECK(inserter, "stream_to failed to initialize"); + PQXX_CHECK_THROWS( + insert_bad_null_tuple(inserter), pqxx::not_null_violation, + "Did not expected not_null_violation when stream_to inserts a bad null."); +} + + +/// Try to violate stream_to_test's not-null construct using a stream_to. +void insert_bad_null_write(pqxx::stream_to &inserter) +{ + inserter.write_values( + nullptr, "now", 4321, ipv4{8, 8, 8, 8}, "hello world", + bytea{'\x00', '\x01', '\x02'}); + inserter.complete(); +} + + +void test_bad_null_fold(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; + PQXX_CHECK(inserter, "stream_to failed to initialize"); + PQXX_CHECK_THROWS( + insert_bad_null_write(inserter), pqxx::not_null_violation, + "Did not expected not_null_violation when stream_to inserts a bad null."); +} + + +void test_too_few_fields(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; + PQXX_CHECK(inserter, "stream_to failed to initialize"); + + try + { + inserter << std::make_tuple(1234, "now", 4321, ipv4{8, 8, 8, 8}); + inserter.complete(); + tx.commit(); + PQXX_CHECK_NOTREACHED("stream_from improperly inserted row"); + } + catch (pqxx::sql_error const &e) + { + std::string what{e.what()}; + if (what.find("missing data for column") == std::string::npos) + throw; + pqxx::test::expected_exception( + "Could not insert row: " + truncate_sql_error(what)); + } +} + +void test_too_few_fields_fold(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; + PQXX_CHECK(inserter, "stream_to failed to initialize"); + + try + { + inserter.write_values(1234, "now", 4321, ipv4{8, 8, 8, 8}); + inserter.complete(); + tx.commit(); + PQXX_CHECK_NOTREACHED("stream_from_fold improperly inserted row"); + } + catch (pqxx::sql_error const &e) + { + std::string what{e.what()}; + if (what.find("missing data for column") == std::string::npos) + throw; + pqxx::test::expected_exception( + "Fold - Could not insert row: " + truncate_sql_error(what)); + } +} + + +void test_too_many_fields(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; + PQXX_CHECK(inserter, "stream_to failed to initialize"); + + try + { + inserter << std::make_tuple( + 1234, "now", 4321, ipv4{8, 8, 8, 8}, "hello world", + bytea{'\x00', '\x01', '\x02'}, 5678); + inserter.complete(); + tx.commit(); + PQXX_CHECK_NOTREACHED("stream_from improperly inserted row"); + } + catch (pqxx::sql_error const &e) + { + std::string what{e.what()}; + if (what.find("extra data") == std::string::npos) + throw; + pqxx::test::expected_exception( + "Could not insert row: " + truncate_sql_error(what)); + } +} + +void test_too_many_fields_fold(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; + PQXX_CHECK(inserter, "stream_to failed to initialize"); + + try + { + inserter.write_values( + 1234, "now", 4321, ipv4{8, 8, 8, 8}, "hello world", + bytea{'\x00', '\x01', '\x02'}, 5678); + inserter.complete(); + tx.commit(); + PQXX_CHECK_NOTREACHED("stream_from_fold improperly inserted row"); + } + catch (pqxx::sql_error const &e) + { + std::string what{e.what()}; + if (what.find("extra data") == std::string::npos) + throw; + pqxx::test::expected_exception( + "Fold - Could not insert row: " + truncate_sql_error(what)); + } +} + + +void test_stream_to_does_nonnull_optional() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + tx.exec0("CREATE TEMP TABLE foo(x integer, y text)"); + auto inserter{pqxx::stream_to::table(tx, {"foo"})}; + inserter.write_values( + std::optional{368}, std::optional{"Text"}); + inserter.complete(); + auto const row{tx.exec1("SELECT x, y FROM foo")}; + PQXX_CHECK_EQUAL( + row[0].as(), "368", "Non-null int optional came out wrong."); + PQXX_CHECK_EQUAL( + row[1].as(), "Text", + "Non-null string optional came out wrong."); +} + + +template class O> +void test_optional(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; + PQXX_CHECK(inserter, "stream_to failed to initialize"); + + inserter << std::make_tuple( + 910, O{pqxx::nullness>::null()}, + O{pqxx::nullness>::null()}, + O{pqxx::nullness>::null()}, "\\N", bytea{}); + + inserter.complete(); + tx.commit(); +} + +template class O> +void test_optional_fold(pqxx::connection &connection) +{ + pqxx::work tx{connection}; + auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; + PQXX_CHECK(inserter, "stream_to failed to initialize"); + + inserter.write_values( + 910, O{pqxx::nullness>::null()}, + O{pqxx::nullness>::null()}, + O{pqxx::nullness>::null()}, "\\N", bytea{}); + + inserter.complete(); + tx.commit(); +} + + +// As an alternative to a tuple, you can also insert a container. +void test_container_stream_to() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + tx.exec0("CREATE TEMP TABLE test_container(a integer, b integer)"); + + auto inserter{pqxx::stream_to::table(tx, {"test_container"})}; + + inserter << std::vector{112, 244}; + inserter.complete(); + + auto read{tx.exec1("SELECT * FROM test_container")}; + PQXX_CHECK_EQUAL( + read[0].as(), 112, "stream_to on container went wrong."); + PQXX_CHECK_EQUAL( + read[1].as(), 244, "Second container field went wrong."); + tx.commit(); +} + +void test_variant_fold(pqxx::connection_base &connection) +{ + pqxx::work tx{connection}; + auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; + PQXX_CHECK(inserter, "stream_to failed to initialize"); + + inserter.write_values( + std::variant{1234}, + std::variant{"now"}, 4321, ipv4{8, 8, 8, 8}, + "hello world", bytea{'\x00', '\x01', '\x02'}); + inserter.write_values( + 5678, "2018-11-17 21:23:00", nullptr, nullptr, + "\u3053\u3093\u306b\u3061\u308f", + bytea{'f', 'o', 'o', ' ', 'b', 'a', 'r', '\0'}); + inserter.write_values(910, nullptr, nullptr, nullptr, "\\N", bytea{}); + + inserter.complete(); + tx.commit(); +} + +void clear_table(pqxx::connection &conn) +{ + pqxx::work tx{conn}; + tx.exec0("DELETE FROM stream_to_test"); + tx.commit(); +} + + +void test_stream_to() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + + tx.exec0( + "CREATE TEMP TABLE stream_to_test (" + "number0 INT NOT NULL," + "ts1 TIMESTAMP NULL," + "number2 INT NULL," + "addr3 INET NULL," + "txt4 TEXT NULL," + "bin5 BYTEA NOT NULL" + ")"); + tx.commit(); + + test_nonoptionals(conn); + clear_table(conn); + test_nonoptionals_fold(conn); + clear_table(conn); + test_bad_null(conn); + clear_table(conn); + test_bad_null_fold(conn); + clear_table(conn); + test_too_few_fields(conn); + clear_table(conn); + test_too_few_fields_fold(conn); + clear_table(conn); + test_too_many_fields(conn); + clear_table(conn); + test_too_many_fields_fold(conn); + clear_table(conn); + test_optional(conn); + clear_table(conn); + test_optional_fold(conn); + clear_table(conn); + test_optional(conn); + clear_table(conn); + test_optional_fold(conn); + clear_table(conn); + test_variant_fold(conn); +} + + +void test_stream_to_factory_with_static_columns() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + + tx.exec0("CREATE TEMP TABLE pqxx_stream_to(a integer, b varchar)"); + + auto stream{pqxx::stream_to::table(tx, {"pqxx_stream_to"}, {"a", "b"})}; + stream.write_values(3, "three"); + stream.complete(); + + auto r{tx.exec1("SELECT a, b FROM pqxx_stream_to")}; + PQXX_CHECK_EQUAL(r[0].as(), 3, "Failed to stream_to a table."); + PQXX_CHECK_EQUAL( + r[1].as(), "three", + "Failed to stream_to a string to a table."); +} + + +void test_stream_to_factory_with_dynamic_columns() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + + tx.exec0("CREATE TEMP TABLE pqxx_stream_to(a integer, b varchar)"); + + std::vector columns{"a", "b"}; +#if defined(PQXX_HAVE_CONCEPTS) + auto stream{pqxx::stream_to::table(tx, {"pqxx_stream_to"}, columns)}; +#else + auto stream{pqxx::stream_to::raw_table( + tx, conn.quote_table({"pqxx_stream_to"}), conn.quote_columns(columns))}; +#endif + stream.write_values(4, "four"); + stream.complete(); + + auto r{tx.exec1("SELECT a, b FROM pqxx_stream_to")}; + PQXX_CHECK_EQUAL( + r[0].as(), 4, "Failed to stream_to a table with dynamic columns."); + PQXX_CHECK_EQUAL( + r[1].as(), "four", + "Failed to stream_to a string to a table with dynamic columns."); +} + + +void test_stream_to_quotes_arguments() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + + std::string const table{R"--(pqxx_Stream"'x)--"}, column{R"--(a'"b)--"}; + + tx.exec0( + "CREATE TEMP TABLE " + tx.quote_name(table) + "(" + tx.quote_name(column) + + " integer)"); + auto write{pqxx::stream_to::table(tx, {table}, {column})}; + write.write_values(12); + write.complete(); + + PQXX_CHECK_EQUAL( + tx.query_value( + "SELECT " + tx.quote_name(column) + " FROM " + tx.quote_name(table)), + 12, "Stream wrote wrong value."); +} + + +PQXX_REGISTER_TEST(test_stream_to); +PQXX_REGISTER_TEST(test_container_stream_to); +PQXX_REGISTER_TEST(test_stream_to_does_nonnull_optional); +PQXX_REGISTER_TEST(test_stream_to_factory_with_static_columns); +PQXX_REGISTER_TEST(test_stream_to_factory_with_dynamic_columns); +PQXX_REGISTER_TEST(test_stream_to_quotes_arguments); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_string_conversion.cxx b/ext/libpqxx-7.7.3/test/unit/test_string_conversion.cxx new file mode 100644 index 000000000..30bc084f5 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_string_conversion.cxx @@ -0,0 +1,178 @@ +#include +#include + +#include +#include + +#include "../test_helpers.hxx" + +// Some enums with string conversions. +enum EnumA +{ + ea0, + ea1, + ea2 +}; +enum EnumB +{ + eb0, + eb1, + eb2 +}; +namespace pqxx +{ +PQXX_DECLARE_ENUM_CONVERSION(EnumA); +PQXX_DECLARE_ENUM_CONVERSION(EnumB); +} // namespace pqxx + + +namespace +{ +// "A minimal difference." +constexpr double thres{0.00001}; + + +void test_string_conversion() +{ + PQXX_CHECK_EQUAL( + "C string array", pqxx::to_string("C string array"), + "C-style string constant does not convert to string properly."); + + char text_array[]{"C char array"}; + PQXX_CHECK_EQUAL( + "C char array", pqxx::to_string(text_array), + "C-style non-const char array does not convert to string properly."); + + char const *text_ptr{"C string pointer"}; + PQXX_CHECK_EQUAL( + "C string pointer", pqxx::to_string(text_ptr), + "C-style string pointer does not convert to string properly."); + + std::string const cxx_string{"C++ string"}; + PQXX_CHECK_EQUAL( + "C++ string", pqxx::to_string(cxx_string), + "C++-style string object does not convert to string properly."); + + PQXX_CHECK_EQUAL("0", pqxx::to_string(0), "Zero does not convert right."); + PQXX_CHECK_EQUAL( + "1", pqxx::to_string(1), "Basic integer does not convert right."); + PQXX_CHECK_EQUAL("-1", pqxx::to_string(-1), "Negative numbers don't work."); + PQXX_CHECK_EQUAL( + "9999", pqxx::to_string(9999), "Larger numbers don't work."); + PQXX_CHECK_EQUAL( + "-9999", pqxx::to_string(-9999), "Larger negative numbers don't work."); + + int x; + pqxx::from_string("0", x); + PQXX_CHECK_EQUAL(0, x, "Zero does not parse right."); + pqxx::from_string("1", x); + PQXX_CHECK_EQUAL(1, x, "Basic integer does not parse right."); + pqxx::from_string("-1", x); + PQXX_CHECK_EQUAL(-1, x, "Negative numbers don't work."); + pqxx::from_string("9999", x); + PQXX_CHECK_EQUAL(9999, x, "Larger numbers don't work."); + pqxx::from_string("-9999", x); + PQXX_CHECK_EQUAL(-9999, x, "Larger negative numbers don't work."); + + // Bug #263 describes a case where this kind of overflow went undetected. + if (sizeof(unsigned int) == 4) + { + std::uint32_t u; + PQXX_CHECK_THROWS( + pqxx::from_string("4772185884", u), pqxx::conversion_error, + "Overflow not detected."); + } + + // We can convert to and from long double. The implementation may fall + // back on a thread-local std::stringstream. Each call does its own + // cleanup, so the conversion works multiple times. + constexpr long double ld1{123456789.25}, ld2{9876543210.5}; + constexpr char lds1[]{"123456789.25"}, lds2[]{"9876543210.5"}; + PQXX_CHECK_EQUAL( + pqxx::to_string(ld1).substr(0, 12), lds1, + "Wrong conversion from long double."); + PQXX_CHECK_EQUAL( + pqxx::to_string(ld2).substr(0, 12), lds2, + "Wrong value on repeated conversion from long double."); + long double ldi1, ldi2; + pqxx::from_string(lds1, ldi1); + PQXX_CHECK_BOUNDS( + ldi1, ld1 - thres, ld1 + thres, "Wrong conversion to long double."); + pqxx::from_string(lds2, ldi2); + PQXX_CHECK_BOUNDS( + ldi2, ld2 - thres, ld2 + thres, + "Wrong repeated conversion to long double."); + + // We can define string conversions for enums. + PQXX_CHECK_EQUAL( + pqxx::to_string(ea0), "0", "Enum-to-string conversion is broken."); + PQXX_CHECK_EQUAL( + pqxx::to_string(eb0), "0", + "Enum-to-string conversion is inconsistent between enum types."); + PQXX_CHECK_EQUAL( + pqxx::to_string(ea1), "1", + "Enum-to-string conversion breaks for nonzero value."); + + EnumA ea; + pqxx::from_string("2", ea); + PQXX_CHECK_EQUAL(ea, ea2, "String-to-enum conversion is broken."); +} + + +void test_convert_variant_to_string() +{ + PQXX_CHECK_EQUAL( + pqxx::to_string(std::variant{99}), "99", + "First variant field did not convert right."); + + PQXX_CHECK_EQUAL( + pqxx::to_string(std::variant{"Text"}), "Text", + "Second variant field did not convert right."); +} + + +void test_integer_conversion() +{ + PQXX_CHECK_EQUAL( + pqxx::from_string("12"), 12, "Basic integer conversion failed."); + PQXX_CHECK_EQUAL( + pqxx::from_string(" 12"), 12, + "Leading whitespace confused integer conversion."); + PQXX_CHECK_THROWS( + pqxx::ignore_unused(pqxx::from_string("")), pqxx::conversion_error, + "Converting empty string to integer did not throw conversion error."); + PQXX_CHECK_THROWS( + pqxx::ignore_unused(pqxx::from_string(" ")), pqxx::conversion_error, + "Converting whitespace to integer did not throw conversion error."); + PQXX_CHECK_EQUAL( + pqxx::from_string("-6"), -6, + "Leading whitespace did not work with negative number."); + PQXX_CHECK_THROWS( + pqxx::ignore_unused(pqxx::from_string("- 3")), pqxx::conversion_error, + "A space between negation and number was not properly flagged."); + PQXX_CHECK_THROWS( + pqxx::ignore_unused(pqxx::from_string("-")), pqxx::conversion_error, + "Just a minus sign should not parse as an integer."); +} + + +void test_convert_null() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + PQXX_CHECK_EQUAL( + tx.quote(nullptr), "NULL", "Null pointer did not come out as SQL 'null'."); + PQXX_CHECK_EQUAL( + tx.quote(std::nullopt), "NULL", + "std::nullopt did not come out as SQL 'null'."); + PQXX_CHECK_EQUAL( + tx.quote(std::monostate{}), "NULL", + "std::monostate did not come out as SQL 'null'."); +} + + +PQXX_REGISTER_TEST(test_string_conversion); +PQXX_REGISTER_TEST(test_convert_variant_to_string); +PQXX_REGISTER_TEST(test_integer_conversion); +PQXX_REGISTER_TEST(test_convert_null); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_subtransaction.cxx b/ext/libpqxx-7.7.3/test/unit/test_subtransaction.cxx new file mode 100644 index 000000000..4ba909bec --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_subtransaction.cxx @@ -0,0 +1,78 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +void make_table(pqxx::transaction_base &trans) +{ + trans.exec0("CREATE TEMP TABLE foo (x INTEGER)"); +} + + +void insert_row(pqxx::transaction_base &trans) +{ + trans.exec0("INSERT INTO foo(x) VALUES (1)"); +} + + +int count_rows(pqxx::transaction_base &trans) +{ + return trans.query_value("SELECT count(*) FROM foo"); +} + + +void test_subtransaction_commits_if_commit_called(pqxx::connection &conn) +{ + pqxx::work trans(conn); + make_table(trans); + { + pqxx::subtransaction sub(trans); + insert_row(sub); + sub.commit(); + } + PQXX_CHECK_EQUAL( + count_rows(trans), 1, "Work done in committed subtransaction was lost."); +} + + +void test_subtransaction_aborts_if_abort_called(pqxx::connection &conn) +{ + pqxx::work trans(conn); + make_table(trans); + { + pqxx::subtransaction sub(trans); + insert_row(sub); + sub.abort(); + } + PQXX_CHECK_EQUAL( + count_rows(trans), 0, "Aborted subtransaction was not rolled back."); +} + + +void test_subtransaction_aborts_implicitly(pqxx::connection &conn) +{ + pqxx::work trans(conn); + make_table(trans); + { + pqxx::subtransaction sub(trans); + insert_row(sub); + } + PQXX_CHECK_EQUAL( + count_rows(trans), 0, + "Uncommitted subtransaction was not rolled back uring destruction."); +} + + +void test_subtransaction() +{ + pqxx::connection conn; + test_subtransaction_commits_if_commit_called(conn); + test_subtransaction_aborts_if_abort_called(conn); + test_subtransaction_aborts_implicitly(conn); +} + + +PQXX_REGISTER_TEST(test_subtransaction); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_test_helpers.cxx b/ext/libpqxx-7.7.3/test/unit/test_test_helpers.cxx new file mode 100644 index 000000000..89cde627e --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_test_helpers.cxx @@ -0,0 +1,214 @@ +#include "../test_helpers.hxx" + +namespace +{ +void empty() {} + + +void test_check_notreached() +{ + // At a minimum, PQXX_CHECK_NOTREACHED must work. + bool failed{true}; + try + { + PQXX_CHECK_NOTREACHED("(expected)"); + failed = false; + } + catch (pqxx::test::test_failure const &) + { + // This is what we expect. + } + if (not failed) + throw pqxx::test::test_failure( + __FILE__, __LINE__, "PQXX_CHECK_NOTREACHED is broken."); +} + + +// Test PQXX_CHECK. +void test_check() +{ + PQXX_CHECK(true, "PQXX_CHECK is broken."); + + bool failed{true}; + try + { + PQXX_CHECK(false, "(expected)"); + failed = false; + } + catch (pqxx::test::test_failure const &) + {} + if (not failed) + PQXX_CHECK_NOTREACHED("PQXX_CHECK failed to notice failure."); +} + + +// Test PQXX_CHECK_THROWS_EXCEPTION. +void test_check_throws_exception() +{ + // PQXX_CHECK_THROWS_EXCEPTION expects std::exception... + PQXX_CHECK_THROWS_EXCEPTION( + throw std::exception(), + "PQXX_CHECK_THROWS_EXCEPTION did not catch std::exception."); + + // ...or any exception type derived from it. + PQXX_CHECK_THROWS_EXCEPTION( + throw pqxx::test::test_failure(__FILE__, __LINE__, "(expected)"), + "PQXX_CHECK_THROWS_EXCEPTION() failed to catch expected exception."); + + // Any other type is an error. + bool failed{true}; + try + { + PQXX_CHECK_THROWS_EXCEPTION(throw 1, "(expected)"); + failed = false; + } + catch (pqxx::test::test_failure const &) + {} + PQXX_CHECK( + failed, + "PQXX_CHECK_THROWS_EXCEPTION did not complain about non-exception."); + + // But there _must_ be an exception. + failed = true; + try + { + // If the test fails to throw, this throws a failure. + PQXX_CHECK_THROWS_EXCEPTION(empty(), "(expected)"); + // So we shouldn't get to this point. + failed = false; + } + catch (pqxx::test::test_failure const &) + { + // Instead, we go straight here. + } + PQXX_CHECK( + failed, "PQXX_CHECK_THROWS_EXCEPTION did not notice missing exception."); + + // PQXX_CHECK_THROWS_EXCEPTION can test itself... + PQXX_CHECK_THROWS_EXCEPTION( + PQXX_CHECK_THROWS_EXCEPTION(empty(), "(expected)"), + "PQXX_CHECK_THROWS_EXCEPTION failed to throw for missing exception."); + + PQXX_CHECK_THROWS_EXCEPTION( + PQXX_CHECK_THROWS_EXCEPTION(throw 1, "(expected)"), + "PQXX_CHECK_THROWS_EXCEPTION ignored wrong exception type."); +} + + +// Test PQXX_CHECK_THROWS. +void test_check_throws() +{ + PQXX_CHECK_THROWS( + throw pqxx::test::test_failure(__FILE__, __LINE__, "(expected)"), + pqxx::test::test_failure, + "PQXX_CHECK_THROWS() failed to catch expected exception."); + + // Even if it's not std::exception-derived. + PQXX_CHECK_THROWS(throw 1, int, "(expected)"); + + // PQXX_CHECK_THROWS means there _must_ be an exception. + bool failed{true}; + try + { + // If the test fails to throw, PQXX_CHECK_THROWS throws a failure. + PQXX_CHECK_THROWS(empty(), std::runtime_error, "(expected)"); + // So we shouldn't get to this point. + failed = false; + } + catch (pqxx::test::test_failure const &) + { + // Instead, we go straight here. + } + PQXX_CHECK(failed, "PQXX_CHECK_THROWS did not notice missing exception."); + + // The exception must be of the right type (or a subclass of the right type). + failed = true; + try + { + // If the test throws the wrong type, PQXX_CHECK_THROWS throws a failure. + PQXX_CHECK_THROWS( + throw std::exception(), pqxx::test::test_failure, "(expected)"); + failed = false; + } + catch (pqxx::test::test_failure const &) + { + // Instead, we go straight here. + } + PQXX_CHECK(failed, "PQXX_CHECK_THROWS did not notice wrong exception type."); + + // PQXX_CHECK_THROWS can test itself... + PQXX_CHECK_THROWS( + PQXX_CHECK_THROWS(empty(), pqxx::test::test_failure, "(expected)"), + pqxx::test::test_failure, + "PQXX_CHECK_THROWS failed to throw for missing exception."); + + PQXX_CHECK_THROWS( + PQXX_CHECK_THROWS(throw 1, std::runtime_error, "(expected)"), + pqxx::test::test_failure, + "PQXX_CHECK_THROWS failed to throw for wrong exception type."); +} + + +void test_test_helpers() +{ + test_check_notreached(); + test_check(); + test_check_throws_exception(); + test_check_throws(); + + // Test other helpers against PQXX_CHECK_THROWS. + PQXX_CHECK_THROWS( + PQXX_CHECK_NOTREACHED("(expected)"), pqxx::test::test_failure, + "PQXX_CHECK_THROWS did not catch PQXX_CHECK_NOTREACHED."); + + PQXX_CHECK_THROWS( + PQXX_CHECK(false, "(expected)"), pqxx::test::test_failure, + "PQXX_CHECK_THROWS did not catch failing PQXX_CHECK."); + + PQXX_CHECK_THROWS( + PQXX_CHECK_THROWS( + PQXX_CHECK(true, "(shouldn't happen)"), pqxx::test::test_failure, + "(expected)"), + pqxx::test::test_failure, + "PQXX_CHECK_THROWS on successful PQXX_CHECK failed to throw."); + + // PQXX_CHECK_EQUAL tests for equality. Its arguments need not be of the + // same type, as long as equality between them is defined. + PQXX_CHECK_EQUAL(1, 1, "PQXX_CHECK_EQUAL is broken."); + PQXX_CHECK_EQUAL(1, 1L, "PQXX_CHECK_EQUAL breaks on type mismatch."); + + PQXX_CHECK_THROWS( + PQXX_CHECK_EQUAL(1, 2, "(expected)"), pqxx::test::test_failure, + "PQXX_CHECK_EQUAL fails to spot inequality."); + + // PQXX_CHECK_NOT_EQUAL is like PQXX_CHECK_EQUAL, but tests for inequality. + PQXX_CHECK_NOT_EQUAL(1, 2, "PQXX_CHECK_NOT_EQUAL is broken."); + PQXX_CHECK_THROWS( + PQXX_CHECK_NOT_EQUAL(1, 1, "(expected)"), pqxx::test::test_failure, + "PQXX_CHECK_NOT_EQUAL fails to fail when arguments are equal."); + PQXX_CHECK_THROWS( + PQXX_CHECK_NOT_EQUAL(1, 1L, "(expected)"), pqxx::test::test_failure, + "PQXX_CHECK_NOT_EQUAL breaks on type mismatch."); + + // PQXX_CHECK_BOUNDS checks a value against a range. + PQXX_CHECK_BOUNDS(2, 1, 3, "PQXX_CHECK_BOUNDS wrongly finds fault."); + + PQXX_CHECK_THROWS( + PQXX_CHECK_BOUNDS(1, 2, 3, "(Expected)"), pqxx::test::test_failure, + "PQXX_CHECK_BOUNDS did not detect value below permitted range."); + + // PQXX_CHECK_BOUNDS tests against a half-open interval. + PQXX_CHECK_BOUNDS(1, 1, 3, "PQXX_CHECK_BOUNDS goes wrong on lower bound."); + PQXX_CHECK_THROWS( + PQXX_CHECK_BOUNDS(3, 1, 3, "(Expected)"), pqxx::test::test_failure, + "PQXX_CHECK_BOUNDS interval is not half-open."); + + // PQXX_CHECK_BOUNDS deals well with empty intervals. + PQXX_CHECK_THROWS( + PQXX_CHECK_BOUNDS(1, 2, 1, "(Expected)"), pqxx::test::test_failure, + "PQXX_CHECK_BOUNDS did not detect empty interval."); +} + + +PQXX_REGISTER_TEST(test_test_helpers); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_thread_safety_model.cxx b/ext/libpqxx-7.7.3/test/unit/test_thread_safety_model.cxx new file mode 100644 index 000000000..cf7627cb3 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_thread_safety_model.cxx @@ -0,0 +1,23 @@ +#include "../test_helpers.hxx" + +#include + +namespace +{ +void test_thread_safety_model() +{ + auto const model{pqxx::describe_thread_safety()}; + + if (model.safe_libpq and model.safe_kerberos) + PQXX_CHECK_EQUAL( + model.description, "", + "Thread-safety looks okay but model description is nonempty."); + else + PQXX_CHECK_NOT_EQUAL( + model.description, "", + "Thread-safety model is imperfect but lacks description."); +} + + +PQXX_REGISTER_TEST(test_thread_safety_model); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_time.cxx b/ext/libpqxx-7.7.3/test/unit/test_time.cxx new file mode 100644 index 000000000..2fb79d472 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_time.cxx @@ -0,0 +1,86 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +#if defined(PQXX_HAVE_YEAR_MONTH_DAY) +using namespace std::literals; + + +void test_date_string_conversion() +{ + pqxx::connection conn; + pqxx::work tx{conn}; + std::tuple const conversions[]{ + {-542, 1, 1, "0543-01-01 BC"sv}, + {-1, 2, 3, "0002-02-03 BC"sv}, + {0, 9, 14, "0001-09-14 BC"sv}, + {1, 12, 8, "0001-12-08"sv}, + {2021, 10, 24, "2021-10-24"sv}, + {10191, 8, 30, "10191-08-30"sv}, + {-4712, 1, 1, "4713-01-01 BC"sv}, + {32767, 12, 31, "32767-12-31"sv}, + {2000, 2, 29, "2000-02-29"sv}, + {2004, 2, 29, "2004-02-29"sv}, + // This one won't work in postgres, but we can test the conversions. + {-32767, 11, 3, "32768-11-03 BC"sv}, + }; + for (auto const &[y, m, d, text] : conversions) + { + std::chrono::year_month_day const date{ + std::chrono::year{y}, std::chrono::month{m}, std::chrono::day{d}}; + PQXX_CHECK_EQUAL( + pqxx::to_string(date), text, "Date did not convert right."); + PQXX_CHECK_EQUAL( + pqxx::from_string(text), date, + "Date did not parse right."); + if (int{date.year()} > -4712) + { + // We can't test this for years before 4713 BC (4712 BCE), because + // postgres doesn't handle earlier years. + PQXX_CHECK_EQUAL( + tx.query_value( + "SELECT '" + pqxx::to_string(date) + "'::date"), + text, "Backend interpreted date differently."); + } + } + + std::string_view const invalid[]{ + ""sv, + "yesterday"sv, + "1981-01"sv, + "2010"sv, + "2010-8-9"sv, + "1900-02-29"sv, + "2021-02-29"sv, + "2000-11-29-3"sv, + "1900-02-29"sv, + "2003-02-29"sv, + "12-12-12"sv, + "0000-09-16"sv, + "-01-01"sv, + "-1000-01-01"sv, + "1000-00-01"sv, + "1000-01-00"sv, + "2001y-01-01"sv, + "10-09-08"sv, + "0-01-01"sv, + "0000-01-01"sv, + "2021-13-01"sv, + "2021-+02-01"sv, + "2021-12-32"sv, + }; + for (auto const text : invalid) + PQXX_CHECK_THROWS( + pqxx::ignore_unused( + pqxx::from_string(text)), + pqxx::conversion_error, + pqxx::internal::concat("Invalid date '", text, "' parsed as if valid.")); +} + + +PQXX_REGISTER_TEST(test_date_string_conversion); +#endif // PQXX_HAVE_YEAR_MONTH_DAY +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_transaction.cxx b/ext/libpqxx-7.7.3/test/unit/test_transaction.cxx new file mode 100644 index 000000000..2ae016a26 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_transaction.cxx @@ -0,0 +1,113 @@ +#include +#include + +#include "../test_helpers.hxx" + + +namespace +{ +void test_nontransaction_continues_after_error() +{ + pqxx::connection c; + pqxx::nontransaction tx{c}; + + PQXX_CHECK_EQUAL( + tx.query_value("SELECT 9"), 9, "Simple query went wrong."); + PQXX_CHECK_THROWS( + tx.exec("SELECT 1/0"), pqxx::sql_error, "Expected error did not happen."); + + PQXX_CHECK_EQUAL( + tx.query_value("SELECT 5"), 5, "Wrong result after error."); +} + + +std::string const table{"pqxx_test_transaction"}; + + +void delete_temp_table(pqxx::transaction_base &tx) +{ + tx.exec0(std::string{"DROP TABLE IF EXISTS "} + table); +} + + +void create_temp_table(pqxx::transaction_base &tx) +{ + tx.exec0("CREATE TEMP TABLE " + table + " (x integer)"); +} + + +void insert_temp_table(pqxx::transaction_base &tx, int value) +{ + tx.exec0( + "INSERT INTO " + table + " (x) VALUES (" + pqxx::to_string(value) + ")"); +} + +int count_temp_table(pqxx::transaction_base &tx) +{ + return tx.query_value("SELECT count(*) FROM " + table); +} + + +void test_nontransaction_autocommits() +{ + pqxx::connection c; + + pqxx::nontransaction tx1{c}; + delete_temp_table(tx1); + create_temp_table(tx1); + tx1.commit(); + + pqxx::nontransaction tx2{c}; + insert_temp_table(tx2, 4); + tx2.abort(); + + pqxx::nontransaction tx3{c}; + PQXX_CHECK_EQUAL( + count_temp_table(tx3), 1, + "Did not keep effect of aborted nontransaction."); + delete_temp_table(tx3); +} + + +template void test_double_close() +{ + pqxx::connection c; + + TX tx1{c}; + tx1.exec1("SELECT 1"); + tx1.commit(); + tx1.commit(); + + TX tx2{c}; + tx2.exec1("SELECT 2"); + tx2.abort(); + tx2.abort(); + + TX tx3{c}; + tx3.exec1("SELECT 3"); + tx3.commit(); + PQXX_CHECK_THROWS( + tx3.abort(), pqxx::usage_error, "Abort after commit not caught."); + ; + + TX tx4{c}; + tx4.exec1("SELECT 4"); + tx4.abort(); + PQXX_CHECK_THROWS( + tx4.commit(), pqxx::usage_error, "Commit after abort not caught."); +} + + +void test_transaction() +{ + test_nontransaction_continues_after_error(); + test_nontransaction_autocommits(); + test_double_close>(); + test_double_close(); + test_double_close(); + test_double_close>(); +} + + +PQXX_REGISTER_TEST(test_transaction); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_transaction_base.cxx b/ext/libpqxx-7.7.3/test/unit/test_transaction_base.cxx new file mode 100644 index 000000000..bea15b190 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_transaction_base.cxx @@ -0,0 +1,106 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_exec0(pqxx::transaction_base &trans) +{ + pqxx::result E{trans.exec0("SELECT * FROM pg_tables WHERE 0 = 1")}; + PQXX_CHECK(std::empty(E), "Nonempty result from exec0."); + + PQXX_CHECK_THROWS( + trans.exec0("SELECT 99"), pqxx::unexpected_rows, + "Nonempty exec0 result did not throw unexpected_rows."); +} + + +void test_exec1(pqxx::transaction_base &trans) +{ + pqxx::row R{trans.exec1("SELECT 99")}; + PQXX_CHECK_EQUAL(std::size(R), 1, "Wrong size result from exec1."); + PQXX_CHECK_EQUAL(R.front().as(), 99, "Wrong result from exec1."); + + PQXX_CHECK_THROWS( + trans.exec1("SELECT * FROM pg_tables WHERE 0 = 1"), pqxx::unexpected_rows, + "Empty exec1 result did not throw unexpected_rows."); + PQXX_CHECK_THROWS( + trans.exec1("SELECT * FROM generate_series(1, 2)"), pqxx::unexpected_rows, + "Two-row exec1 result did not throw unexpected_rows."); +} + + +void test_exec_n(pqxx::transaction_base &trans) +{ + pqxx::result R{trans.exec_n(3, "SELECT * FROM generate_series(1, 3)")}; + PQXX_CHECK_EQUAL(std::size(R), 3, "Wrong result size from exec_n."); + + PQXX_CHECK_THROWS( + trans.exec_n(2, "SELECT * FROM generate_series(1, 3)"), + pqxx::unexpected_rows, + "exec_n did not throw unexpected_rows for an undersized result."); + PQXX_CHECK_THROWS( + trans.exec_n(4, "SELECT * FROM generate_series(1, 3)"), + pqxx::unexpected_rows, + "exec_n did not throw unexpected_rows for an oversized result."); +} + + +void test_query_value(pqxx::connection &conn) +{ + pqxx::work tx{conn}; + + PQXX_CHECK_EQUAL( + tx.query_value("SELECT 84 / 2"), 42, + "Got wrong value from query_value."); + PQXX_CHECK_THROWS( + tx.query_value("SAVEPOINT dummy"), pqxx::unexpected_rows, + "Got field when none expected."); + PQXX_CHECK_THROWS( + tx.query_value("SELECT generate_series(1, 2)"), pqxx::unexpected_rows, + "Failed to fail for multiple rows."); + PQXX_CHECK_THROWS( + tx.query_value("SELECT 1, 2"), pqxx::usage_error, + "No error for too many fields."); + PQXX_CHECK_THROWS( + tx.query_value("SELECT 3.141"), pqxx::conversion_error, + "Got int field from float string."); +} + + +void test_transaction_base() +{ + pqxx::connection conn; + { + pqxx::work tx{conn}; + test_exec_n(tx); + test_exec0(tx); + test_exec1(tx); + } + test_query_value(conn); +} + + +void test_transaction_for_each() +{ + constexpr auto query{ + "SELECT i, concat('x', (2*i)::text) " + "FROM generate_series(1, 3) AS i " + "ORDER BY i"}; + pqxx::connection conn; + pqxx::work tx{conn}; + std::string ints; + std::string strings; + tx.for_each(query, [&ints, &strings](int i, std::string const &s) { + ints += pqxx::to_string(i) + " "; + strings += s + " "; + }); + PQXX_CHECK_EQUAL(ints, "1 2 3 ", "Unexpected int sequence."); + PQXX_CHECK_EQUAL(strings, "x2 x4 x6 ", "Unexpected string sequence."); +} + + +PQXX_REGISTER_TEST(test_transaction_base); +PQXX_REGISTER_TEST(test_transaction_for_each); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_transaction_focus.cxx b/ext/libpqxx-7.7.3/test/unit/test_transaction_focus.cxx new file mode 100644 index 000000000..48fdfdd3f --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_transaction_focus.cxx @@ -0,0 +1,53 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +auto make_focus(pqxx::dbtransaction &tx) +{ + return pqxx::stream_from::query(tx, "SELECT * from generate_series(1, 10)"); +} + + +void test_cannot_run_statement_during_focus() +{ + pqxx::connection conn; + pqxx::transaction tx{conn}; + tx.exec("SELECT 1"); + auto focus{make_focus(tx)}; + PQXX_CHECK_THROWS( + tx.exec("SELECT 1"), pqxx::usage_error, + "Command during focus did not throw expected error."); +} + + +void test_cannot_run_prepared_statement_during_focus() +{ + pqxx::connection conn; + conn.prepare("foo", "SELECT 1"); + pqxx::transaction tx{conn}; + tx.exec_prepared("foo"); + auto focus{make_focus(tx)}; + PQXX_CHECK_THROWS( + tx.exec_prepared("foo"), pqxx::usage_error, + "Prepared statement during focus did not throw expected error."); +} + +void test_cannot_run_params_statement_during_focus() +{ + pqxx::connection conn; + pqxx::transaction tx{conn}; + tx.exec_params("select $1", 10); + auto focus{make_focus(tx)}; + PQXX_CHECK_THROWS( + tx.exec_params("select $1", 10), pqxx::usage_error, + "Parameterized statement during focus did not throw expected error."); +} + + +PQXX_REGISTER_TEST(test_cannot_run_statement_during_focus); +PQXX_REGISTER_TEST(test_cannot_run_prepared_statement_during_focus); +PQXX_REGISTER_TEST(test_cannot_run_params_statement_during_focus); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_transactor.cxx b/ext/libpqxx-7.7.3/test/unit/test_transactor.cxx new file mode 100644 index 000000000..43034807e --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_transactor.cxx @@ -0,0 +1,130 @@ +#include +#include + +#include "../test_helpers.hxx" + +namespace +{ +void test_transactor_newstyle_executes_simple_query() +{ + pqxx::connection conn; + auto const r{pqxx::perform([&conn] { + return pqxx::work{conn}.exec("SELECT generate_series(1, 4)"); + })}; + + PQXX_CHECK_EQUAL(std::size(r), 4, "Unexpected result size."); + PQXX_CHECK_EQUAL(r.columns(), 1, "Unexpected number of columns."); + PQXX_CHECK_EQUAL(r[0][0].as(), 1, "Unexpected first row."); + PQXX_CHECK_EQUAL(r[3][0].as(), 4, "Unexpected last row."); +} + + +void test_transactor_newstyle_can_return_void() +{ + bool done{false}; + pqxx::perform([&done]() noexcept { done = true; }); + PQXX_CHECK(done, "Callback was not executed."); +} + + +void test_transactor_newstyle_completes_upon_success() +{ + int attempts{0}; + pqxx::perform([&attempts]() noexcept { attempts++; }); + PQXX_CHECK_EQUAL(attempts, 1, "Successful transactor didn't run 1 time."); +} + + +void test_transactor_newstyle_retries_broken_connection() +{ + int counter{0}; + auto const &callback{[&counter] { + ++counter; + if (counter == 1) + throw pqxx::broken_connection(); + return counter; + }}; + + int const result{pqxx::perform(callback)}; + PQXX_CHECK_EQUAL(result, 2, "Transactor run returned wrong result."); + PQXX_CHECK_EQUAL(counter, result, "Number of retries does not match."); +} + + +void test_transactor_newstyle_retries_rollback() +{ + int counter{0}; + auto const &callback{[&counter] { + ++counter; + if (counter == 1) + throw pqxx::transaction_rollback("Simulated error"); + return counter; + }}; + + int const result{pqxx::perform(callback)}; + PQXX_CHECK_EQUAL(result, 2, "Transactor run returned wrong result."); + PQXX_CHECK_EQUAL(counter, result, "Number of retries does not match."); +} + + +void test_transactor_newstyle_does_not_retry_in_doubt_error() +{ + int counter{0}; + auto const &callback{[&counter] { + ++counter; + throw pqxx::in_doubt_error("Simulated error"); + }}; + + PQXX_CHECK_THROWS( + pqxx::perform(callback), pqxx::in_doubt_error, + "Transactor did not propagate in_doubt_error."); + PQXX_CHECK_EQUAL(counter, 1, "Transactor retried after in_doubt_error."); +} + + +void test_transactor_newstyle_does_not_retry_other_error() +{ + int counter{0}; + auto const &callback{[&counter] { + ++counter; + throw std::runtime_error("Simulated error"); + }}; + + PQXX_CHECK_THROWS( + pqxx::perform(callback), std::runtime_error, + "Transactor did not propagate std exception."); + PQXX_CHECK_EQUAL(counter, 1, "Transactor retried after std exception."); +} + + +void test_transactor_newstyle_repeats_up_to_given_number_of_attempts() +{ + int const attempts{5}; + int counter{0}; + auto const &callback{[&counter] { + ++counter; + throw pqxx::transaction_rollback("Simulated error"); + }}; + + PQXX_CHECK_THROWS( + pqxx::perform(callback, attempts), pqxx::transaction_rollback, + "Not propagating original exception."); + PQXX_CHECK_EQUAL(counter, attempts, "Number of retries does not match."); +} + + +void test_transactor() +{ + test_transactor_newstyle_executes_simple_query(); + test_transactor_newstyle_can_return_void(); + test_transactor_newstyle_completes_upon_success(); + test_transactor_newstyle_retries_broken_connection(); + test_transactor_newstyle_retries_rollback(); + test_transactor_newstyle_does_not_retry_in_doubt_error(); + test_transactor_newstyle_does_not_retry_other_error(); + test_transactor_newstyle_repeats_up_to_given_number_of_attempts(); +} + + +PQXX_REGISTER_TEST(test_transactor); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_type_name.cxx b/ext/libpqxx-7.7.3/test/unit/test_type_name.cxx new file mode 100644 index 000000000..af1c93eb5 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_type_name.cxx @@ -0,0 +1,19 @@ +#include "../test_helpers.hxx" + +namespace +{ +void test_type_name() +{ + // It's hard to test in more detail, because spellings may differ. + // For instance, one compiler might call "const unsigned int*" what another + // might call "unsigned const *". And Visual Studio prefixes "class" to + // class types. + std::string const i{pqxx::type_name}; + PQXX_CHECK_LESS(std::size(i), 5u, "type_name is suspiciously long."); + PQXX_CHECK_EQUAL( + i.substr(0, 1), "i", "type_name does not start with 'i'."); +} + + +PQXX_REGISTER_TEST(test_type_name); +} // namespace diff --git a/ext/libpqxx-7.7.3/test/unit/test_zview.cxx b/ext/libpqxx-7.7.3/test/unit/test_zview.cxx new file mode 100644 index 000000000..f8ce5b9e1 --- /dev/null +++ b/ext/libpqxx-7.7.3/test/unit/test_zview.cxx @@ -0,0 +1,16 @@ +#include + +#include "../test_helpers.hxx" + + +namespace +{ +void test_zview_literal() +{ + using pqxx::operator"" _zv; + + PQXX_CHECK_EQUAL(("foo"_zv), pqxx::zview{"foo"}, "zview literal is broken."); +} + +PQXX_REGISTER_TEST(test_zview_literal); +} // namespace diff --git a/ext/libpqxx-7.7.3/tools/Makefile.am b/ext/libpqxx-7.7.3/tools/Makefile.am new file mode 100644 index 000000000..9f918cd5a --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/Makefile.am @@ -0,0 +1,20 @@ +EXTRA_DIST = \ + extract_version \ + lint \ + rmlo.cxx \ + splitconfig \ + template2mak.py \ + pqxxthreadsafety.cxx + +AM_CPPFLAGS=-I$(top_builddir)/include -I$(top_srcdir)/include ${POSTGRES_INCLUDE} +# Override automatically generated list of default includes. It contains only +# unnecessary entries, and incorrectly mentions include/pqxx directly. +DEFAULT_INCLUDES= + +noinst_PROGRAMS = rmlo pqxxthreadsafety + +rmlo_SOURCES = rmlo.cxx +rmlo_LDADD = $(top_builddir)/src/libpqxx.la ${POSTGRES_LIB} + +pqxxthreadsafety_SOURCES = pqxxthreadsafety.cxx +pqxxthreadsafety_LDADD = $(top_builddir)/src/libpqxx.la ${POSTGRES_LIB} diff --git a/ext/libpqxx-7.7.3/tools/Makefile.in b/ext/libpqxx-7.7.3/tools/Makefile.in new file mode 100644 index 000000000..4e21cb265 --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/Makefile.in @@ -0,0 +1,638 @@ +# Makefile.in generated by automake 1.16.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +noinst_PROGRAMS = rmlo$(EXEEXT) pqxxthreadsafety$(EXEEXT) +subdir = tools +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/config/m4/libtool.m4 \ + $(top_srcdir)/config/m4/ltoptions.m4 \ + $(top_srcdir)/config/m4/ltsugar.m4 \ + $(top_srcdir)/config/m4/ltversion.m4 \ + $(top_srcdir)/config/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/include/pqxx/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +PROGRAMS = $(noinst_PROGRAMS) +am_pqxxthreadsafety_OBJECTS = pqxxthreadsafety.$(OBJEXT) +pqxxthreadsafety_OBJECTS = $(am_pqxxthreadsafety_OBJECTS) +pqxxthreadsafety_DEPENDENCIES = $(top_builddir)/src/libpqxx.la +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am_rmlo_OBJECTS = rmlo.$(OBJEXT) +rmlo_OBJECTS = $(am_rmlo_OBJECTS) +rmlo_DEPENDENCIES = $(top_builddir)/src/libpqxx.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/pqxxthreadsafety.Po \ + ./$(DEPDIR)/rmlo.Po +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(pqxxthreadsafety_SOURCES) $(rmlo_SOURCES) +DIST_SOURCES = $(pqxxthreadsafety_SOURCES) $(rmlo_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/config/depcomp \ + $(top_srcdir)/config/mkinstalldirs +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PG_CONFIG = @PG_CONFIG@ +PKG_CONFIG = @PKG_CONFIG@ +POSTGRES_INCLUDE = @POSTGRES_INCLUDE@ +PQXXVERSION = @PQXXVERSION@ +PQXX_ABI = @PQXX_ABI@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_postgres_lib = @with_postgres_lib@ +EXTRA_DIST = \ + extract_version \ + lint \ + rmlo.cxx \ + splitconfig \ + template2mak.py \ + pqxxthreadsafety.cxx + +AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include ${POSTGRES_INCLUDE} +# Override automatically generated list of default includes. It contains only +# unnecessary entries, and incorrectly mentions include/pqxx directly. +DEFAULT_INCLUDES = +rmlo_SOURCES = rmlo.cxx +rmlo_LDADD = $(top_builddir)/src/libpqxx.la ${POSTGRES_LIB} +pqxxthreadsafety_SOURCES = pqxxthreadsafety.cxx +pqxxthreadsafety_LDADD = $(top_builddir)/src/libpqxx.la ${POSTGRES_LIB} +all: all-am + +.SUFFIXES: +.SUFFIXES: .cxx .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tools/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu tools/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +pqxxthreadsafety$(EXEEXT): $(pqxxthreadsafety_OBJECTS) $(pqxxthreadsafety_DEPENDENCIES) $(EXTRA_pqxxthreadsafety_DEPENDENCIES) + @rm -f pqxxthreadsafety$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(pqxxthreadsafety_OBJECTS) $(pqxxthreadsafety_LDADD) $(LIBS) + +rmlo$(EXEEXT): $(rmlo_OBJECTS) $(rmlo_DEPENDENCIES) $(EXTRA_rmlo_DEPENDENCIES) + @rm -f rmlo$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(rmlo_OBJECTS) $(rmlo_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pqxxthreadsafety.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rmlo.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cxx.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cxx.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cxx.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/pqxxthreadsafety.Po + -rm -f ./$(DEPDIR)/rmlo.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/pqxxthreadsafety.Po + -rm -f ./$(DEPDIR)/rmlo.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ext/libpqxx-7.7.3/tools/deprecations b/ext/libpqxx-7.7.3/tools/deprecations new file mode 100755 index 000000000..f3970838b --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/deprecations @@ -0,0 +1,6 @@ +#! /bin/sh +set -eu + +MARKER='include.*ignore-deprecated-pre' +FILES="src include tools/*.cxx test config-tests" +grep -Ircl $MARKER $FILES | sort diff --git a/ext/libpqxx-7.7.3/tools/extract_version b/ext/libpqxx-7.7.3/tools/extract_version new file mode 100755 index 000000000..920ed9aad --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/extract_version @@ -0,0 +1,73 @@ +#! /bin/sh +set -eu + +ARG="${1:-}" + +# Source directory. In out-of-tree builds, Automake sets this for us. +srcdir=${srcdir:-.} + + +# Print usage information. +usage() { + cat <... + +Acceptable option values are: + -h, --help Print this message, and exit. + -a, --abi Show libpqxx ABI version; leave out revision number. + -f, --full Show full libpqxx version string (the default). + -M, --major Show major libpqxx version. + -m, --minor Show minor libpqxx version (between major and revision). +EOF +} + + +# Print "unknown argument" error. +unknown_arg() { + cat <&2 +Unknown argument: $1. +Try + + $0 --help + +for usage information. +EOF +} + + +case "$ARG" in +''|-f|--full) + # Default: Print full version. + cat $srcdir/VERSION + ;; + +-h|--help) + # Print usage information, and exit. + usage + exit + ;; + +-a|--abi) + # Print just the ABI version (major & minor). + sed -e 's/^\([^.]*\.[^.]*\)\..*/\1/' $srcdir/VERSION + ;; + +-M|--major) + # Print the major version number. + sed -e 's/^\([^.]*\)\..*/\1/' $srcdir/VERSION + ;; + +-m|--minor) + # Print the minor version number. + sed -e 's/^[^.]*\.\([^.]*\)\..*/\1/' $srcdir/VERSION + ;; + +*) + unknown_arg $ARG + exit 1 + ;; +esac diff --git a/ext/libpqxx-7.7.3/tools/format b/ext/libpqxx-7.7.3/tools/format new file mode 100755 index 000000000..2645ccdf6 --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/format @@ -0,0 +1,20 @@ +#! /bin/bash +# +# Reformat source code using clang-format. +# +# This script is not portable: as of Ubuntu 21.04, virtualenv's "activate" +# seems to rely on a non-POSIX variable, $OSTYPE. + +set -C -u -e + +# Reformat C++ files. +find -name \*.cxx -o -name \*.hxx | xargs clang-format -i + + +# Reformat CMake files. +WORKDIR=$(mktemp -d) +virtualenv -q --python=$(which python3) "$WORKDIR/venv" +. "$WORKDIR/venv/bin/activate" +pip install -q six pyaml cmake-format +(find -name CMakeLists.txt | xargs cmake-format -i) || /bin/true +rm -rf "$WORKDIR" diff --git a/ext/libpqxx-7.7.3/tools/lint b/ext/libpqxx-7.7.3/tools/lint new file mode 100755 index 000000000..7beadbb27 --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/lint @@ -0,0 +1,197 @@ +#! /bin/bash +# +# Routine sanity checks for libpqxx source tree. +# +# Optionally, set environment variable "srcdir" to the source directory. It +# defaults to the parent directory of the one where this script is. This trick +# requires bash (or a close equivalent) as the shell. + +set -eu -o pipefail + +SRCDIR="${srcdir:-$(dirname "${BASH_SOURCE[0]}")/..}" +PQXXVERSION="$(cd "$SRCDIR" && "$SRCDIR/tools/extract_version")" + +ARGS="${1:-}" + + +# Check that all source code is ASCII. +# +# I'd love to have rich Unicode, but I can live without it. But we don't want +# any surprises in contributions. +check_ascii() { + local exotics=$( + find -name \*.cxx -o -name \*.hxx | + xargs cat | + tr -d '\011-\176' | + wc -c + ) + if [ $exotics != 0 ] + then + echo >&2 "There's a non-ASCII character somewhere." + exit 1 + fi +} + + +# This version must be at the top of the NEWS file. +check_news_version() { + if ! head -n1 $SRCDIR/NEWS | grep -q "^$PQXXVERSION\$" + then + cat <&2 +Version $PQXXVERSION is not at the top of NEWS. +EOF + exit 1 + fi +} + + +# Count number of times header $1 is included from each of given input files. +# Output is lines of :, one line per file, sorted. +count_includes() { + local HEADER_NAME WS PAT + HEADER_NAME="$1" + shift + WS="[[:space:]]*" + PAT="^${WS}#${WS}include${WS}[<\"]$HEADER_NAME[>\"]" + # It's OK for the grep to fail. + (grep -c "$PAT" $* || /bin/true) | sort +} + + +# Check that any includes of $1-pre.hxx are matched by $1-post.hxx ones. +match_pre_post_headers() { + local NAME TEMPDIR PRE POST HEADERS + NAME="$1" + TEMPDIR="$(mktemp -d)" + if test -z "$TEMPDIR" + then + echo >&2 "Could not create temporary directory." + exit 1 + fi + PRE="$TEMPDIR/pre" + POST="$TEMPDIR/post" + HEADERS=$(find include/pqxx/* -type f | grep -v '\.swp$') + count_includes \ + $SRCDIR/NAME-pre.hxx $HEADERS >"$PRE" + count_includes \ + $SRCDIR/NAME-post.hxx $HEADERS >"$POST" + DIFF="$(diff "$PRE" "$POST")" || /bin/true + rm -r -- "$TEMPDIR" + if test -n "$DIFF" + then + cat <&2 +Mismatched pre/post header pairs: + +$DIFF +EOF + exit 1 + fi +} + + +# Any file that includes header-pre.hxx must also include header-post.hxx, and +# vice versa. Similar for ignore-deprecated-{pre|post}.hxx. +check_compiler_internal_headers() { + match_pre_post_headers "pqxx/internal/header" + match_pre_post_headers "pqxx/internal/ignore-deprecated" +} + + +cpplint() { + local cxxflags dialect includes + + if which clang-tidy >/dev/null + then + if [ -e compile_flags ] + then + # Pick out relevant flags, but leave out the rest. + # If we're not compiling with clang, compile_flags may contain + # options that clang-tidy doesn't recognise. + dialect="$(grep -o -- '-std=[^[:space:]]*' compile_flags || true)" + includes="$( + grep -o -- '-I[[:space:]]*[^[:space:]]*' compile_flags || + true)" + else + dialect="" + includes="" + fi + + cxxflags="$dialect $includes" + +# TODO: Please, is there any way we can parallelise this? +# TODO: I'd like cppcoreguidelines-*, but it's a tsunami of false positives. +# TODO: Some useful checks in abseil-*, but it recommends "use our library." +# TODO: Check test/, but tolerate some of the dubious stuff tests do. + clang-tidy \ + $(find $SRCDIR/src $SRCDIR/tools -name \*.cxx) \ + --checks=boost-*, \ + -- \ + -I$SRCDIR/include -Iinclude $cxxflags + fi + + # Run Facebook's "infer" static analyser, if available. + # Instructions here: https://fbinfer.com/docs/getting-started/ + if which infer >/dev/null + then + # This will work in an out-of-tree build, but either way it does + # require a successful "configure", or a cmake with the "make" + # generator. + infer capture -- make -j$(nproc) + infer run + fi +} + + +pylint() { + local PYFILES="$SRCDIR/tools/*.py $SRCDIR/tools/splitconfig" + echo "Skipping pocketlint; it's not up to date with Python3." + # if which pocketlint >/dev/null + # then + # pocketlint $PYFILES + # fi + + if which pyflakes3 >/dev/null + then + pyflakes3 $PYFILES + fi +} + + +main() { + local full="no" + for arg in $ARGS + do + case $arg in + -h|--help) + cat <&2 "Unknown argument: '$arg'" + exit 1 + ;; + esac + done + + check_ascii + pylint + check_news_version + check_compiler_internal_headers + if [ $full == "yes" ] + then + cpplint + fi +} + + +main diff --git a/ext/libpqxx-7.7.3/tools/m4esc.py b/ext/libpqxx-7.7.3/tools/m4esc.py new file mode 100755 index 000000000..355169db2 --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/m4esc.py @@ -0,0 +1,70 @@ +#! /usr/bin/env python3 + +"""M4-quote text, for use as a literal in configure.ac. + +Produces M4 "code" which evaluates to the input text. + +It's not easy to read plain text from an input file in M4, without having it +expanded as M4. Sometimes all we want is literal text! +""" +from __future__ import ( + absolute_import, + print_function, + unicode_literals, + ) + +from argparse import ArgumentParser +from sys import ( + stdin, + stdout, + ) + + +def parse_args(): + parser = ArgumentParser(description=__doc__) + parser.add_argument( + '--open', '-a', default='[[', help="Current open-quote symbol.") + parser.add_argument( + '--close', '-b', default=']]', help="Current close-quote symbol.") + parser.add_argument( + '--input', '-i', default='-', help="Input file, or '-' for stdin.") + parser.add_argument( + '--output', '-o', default='-', help="Output file, or '-' for stdout.") + return parser.parse_args() + + +def open_input(in_file): + if in_file == '-': + return stdin + else: + return open(in_file) + + +def open_output(out_file): + if out_file == '-': + return stdout + else: + return open(out_file, 'w') + + +def escape(line): + return ( + line + .replace('[', '@<:@') + .replace(']', '@:>@') + .replace('#', '@%:@') + .replace('$', '@S|@') + ) + + +def main(args): + with open_input(args.input) as istr, open_output(args.output) as ostr: + ostr.write(args.open) + for line in istr: + ostr.write(escape(line)) + ostr.write('\n') + ostr.write(args.close) + + +if __name__ == '__main__': + main(parse_args()) diff --git a/ext/libpqxx-7.7.3/tools/pqxxthreadsafety.cxx b/ext/libpqxx-7.7.3/tools/pqxxthreadsafety.cxx new file mode 100644 index 000000000..afa8fb01f --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/pqxxthreadsafety.cxx @@ -0,0 +1,10 @@ +// Print thread-safety information for present libpqxx build. +#include + +#include "pqxx/util" + + +int main() +{ + std::cout << pqxx::describe_thread_safety().description << std::endl; +} diff --git a/ext/libpqxx-7.7.3/tools/rmlo.cxx b/ext/libpqxx-7.7.3/tools/rmlo.cxx new file mode 100644 index 000000000..5db9605f1 --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/rmlo.cxx @@ -0,0 +1,39 @@ +// Remove large objects given on the command line from the default database. +#include + +#include "pqxx/pqxx" + + +int main(int, char *argv[]) +{ + pqxx::connection conn; + bool failures = false; + + try + { + for (int i{1}; argv[i]; ++i) + { + auto o{pqxx::from_string(argv[i])}; + try + { + pqxx::perform([o, &conn] { + pqxx::work tx{conn}; + pqxx::blob::remove(tx, o); + tx.commit(); + }); + } + catch (std::exception const &e) + { + std::cerr << e.what() << std::endl; + failures = true; + } + } + } + catch (std::exception const &e) + { + std::cerr << e.what() << std::endl; + return 2; + } + + return failures; +} diff --git a/ext/libpqxx-7.7.3/tools/splitconfig b/ext/libpqxx-7.7.3/tools/splitconfig new file mode 100755 index 000000000..2f4be957f --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/splitconfig @@ -0,0 +1,244 @@ +#! /usr/bin/env python3 + +"""Extract configuration items into various configuration headers. + +This uses the configitems file, a database consisting of text lines with the +following single-tab-separated fields: + - Name of the configuration item, e.g. PQXX_HAVE_PTRDIFF_T. + - Publication marker: public or internal. + - A single environmental factor determining the item, e.g. libpq or compiler. +""" + +from __future__ import ( + absolute_import, + print_function, + unicode_literals, + ) + +from argparse import ArgumentParser +import codecs +from errno import ENOENT +import os.path +from os import getcwd +import re +from sys import ( + getdefaultencoding, + getfilesystemencoding, + stdout, + ) + +__metaclass__ = type + + +def guess_fs_encoding(): + """Try to establish the filesystem encoding. + + It's a sad thing: some guesswork is involved. The encoding often seems to + be conservatively, and incorrectly, set to ascii. + """ + candidates = [ + getfilesystemencoding(), + getdefaultencoding(), + 'utf-8', + ] + for encoding in candidates: + lower = encoding.lower() + if lower != 'ascii' and lower != 'ansi_x3.4-1968': + return encoding + raise AssertionError("unreachable code reached.") + + +def guess_output_encoding(): + """Return the encoding of standard output.""" + # Apparently builds in Docker containers may have None as an encoding. + # Fall back to ASCII. If this ever happens in a non-ASCII path, well, + # there may be a more difficult decision to be made. We'll burn that + # bridge when we get to it, as they almost say. + return stdout.encoding or 'ascii' + + +def decode_path(path): + """Decode a path element from bytes to unicode string.""" + return path.decode(guess_fs_encoding()) + + +def encode_path(path): + """Encode a path element from unicode string to bytes.""" + # Nasty detail: unicode strings are stored as UTF-16. Which can contain + # surrogate pairs. And those break in encoding, unless you use this + # special error handler. + return path.encode(guess_fs_encoding(), 'surrogateescape') + + +def read_text_file(path, encoding='utf-8'): + """Read text file, return as string, or `None` if file is not there.""" + assert isinstance(path, type('')) + try: + with codecs.open(encode_path(path), encoding=encoding) as stream: + return stream.read() + except IOError as error: + if error.errno == ENOENT: + return None + else: + raise + + +def read_lines(path, encoding='utf-8'): + """Read text file, return as list of lines.""" + assert isinstance(path, type('')) + with codecs.open(encode_path(path), encoding=encoding) as stream: + return list(stream) + + +def read_configitems(filename): + """Read the configuration-items database. + + :param filename: Path to the configitems file. + :return: Sequence of text lines from configitems file. + """ + return [line.split() for line in read_lines(filename)] + + +def map_configitems(items): + """Map each config item to publication/factor. + + :param items: Sequence of config items: (name, publication, factor). + :return: Dict mapping each item name to a tuple (publication, factor). + """ + return { + item: (publication, factor) + for item, publication, factor in items + } + + +def read_header(source_tree, filename): + """Read the original config.h generated by autoconf. + + :param source_tree: Path to libpqxx source tree. + :param filename: Path to the config.h file. + :return: Sequence of text lines from config.h. + """ + assert isinstance(source_tree, type('')) + assert isinstance(filename, type('')) + return read_lines(os.path.join(source_tree, filename)) + + +def extract_macro_name(config_line): + """Extract a cpp macro name from a configuration line. + + :param config_line: Text line from config.h which may define a macro. + :return: Name of macro defined in `config_line` if it is a `#define` + statement, or None. + """ + config_line = config_line.strip() + match = re.match('\s*#\s*define\s+([^\s]+)', config_line) + if match is None: + return None + else: + return match.group(1) + + +def extract_section(header_lines, items, publication, factor): + """Extract config items for given publication/factor from header lines. + + :param header_lines: Sequence of header lines from config.h. + :param items: Dict mapping macro names to (publication, factor). + :param publication: Extract only macros for this publication tag. + :param factor: Extract only macros for this environmental factor. + :return: Sequence of `#define` lines from `header_lines` insofar they + fall within the requested section. + """ + return sorted( + line.strip() + for line in header_lines + if items.get(extract_macro_name(line)) == (publication, factor) + ) + + +def compose_header(lines, publication, factor): + """Generate header text containing given lines.""" + intro = ( + "/* Automatically generated from config.h: %s/%s config. */" + % (publication, factor) + ) + return '\n'.join([intro, ''] + lines + ['']) + + +def generate_config(source_tree, header_lines, items, publication, factor): + """Generate config file for a given section, if appropriate. + + Writes nothing if the configuration file ends up identical to one that's + already there. + + :param source_tree: Location of the libpqxx source tree. + :param header_lines: Sequence of header lines from config.h. + :param items: Dict mapping macro names to (publication, factor). + :param publication: Extract only macros for this publication tag. + :param factor: Extract only macros for this environmental factor. + """ + assert isinstance(source_tree, type('')) + config_file = os.path.join( + source_tree, 'include', 'pqxx', + 'config-%s-%s.h' % (publication, factor)) + unicode_path = config_file.encode(guess_output_encoding(), 'replace') + section = extract_section(header_lines, items, publication, factor) + contents = compose_header(section, publication, factor) + if read_text_file(config_file) == contents: + print("Generating %s: no changes--skipping." % unicode_path) + return + + print("Generating %s: %d item(s)." % (unicode_path, len(section))) + path = encode_path(config_file) + with codecs.open(path, 'wb', encoding='ascii') as header: + header.write(contents) + + +def parse_args(): + """Parse command-line arguments.""" + default_source_tree = os.path.dirname( + os.path.dirname(os.path.normpath(os.path.abspath(__file__)))) + parser = ArgumentParser(description=__doc__) + parser.add_argument( + 'sourcetree', metavar='PATH', default=default_source_tree, + help="Location of libpqxx source tree. Defaults to '%(default)s'.") + return parser.parse_args() + + +def check_args(args): + """Validate command-line arguments.""" + if not os.path.isdir(args.sourcetree): + raise Exception("Not a directory: '%s'." % args.sourcetree) + + +def get_current_dir(): + cwd = getcwd() + if isinstance(cwd, bytes): + return decode_path(cwd) + else: + return cwd + + +def main(): + """Main program entry point.""" + args = parse_args() + check_args(args) + # The configitems file is under revision control; it's in sourcetree. + items = read_configitems(os.path.join(args.sourcetree, 'configitems')) + publications = sorted(set(item[1] for item in items)) + factors = sorted(set(item[2] for item in items)) + # The config.h header is generated; it's in the build tree, which should + # be where we are. + directory = get_current_dir() + original_header = read_header( + directory, + os.path.join('include', 'pqxx', 'config.h')) + items_map = map_configitems(items) + + for publication in publications: + for factor in factors: + generate_config( + directory, original_header, items_map, publication, factor) + + +if __name__ == '__main__': + main() diff --git a/ext/libpqxx-7.7.3/tools/template2mak.py b/ext/libpqxx-7.7.3/tools/template2mak.py new file mode 100755 index 000000000..9a5286d95 --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/template2mak.py @@ -0,0 +1,194 @@ +#! /usr/bin/env python3 +"""Minimal macro processor. Used for generating VC++ makefiles. + +The available template commands are: + + Expand a template section for each file in a list of file patterns:: + ###MAKTEMPLATE:FOREACH my/path*/*.cxx,other*.cxx + ... + ###MAKTEMPLATE:ENDFOREACH + + In the template section, you can use `###BASENAME###` to get the base name + of the file being processed (e.g. "base" for "../base.cxx"), and you can + use `###FILENAME###` to get the full filename. + + +Copyright (c) 2000-2022, Bart Samwel and Jeroen T. Vermeulen. +""" + +from __future__ import ( + absolute_import, + print_function, + unicode_literals, + ) + +from argparse import ( + ArgumentError, + ArgumentParser, + RawDescriptionHelpFormatter, + ) +from contextlib import contextmanager +from glob import glob +import os +from sys import ( + argv, + stdin, + stderr, + stdout, + ) +import sys +from textwrap import dedent + + +def expand_foreach_file(path, block, outfile): + """Expand a "foreach" block for a single file path. + + Write the results to outfile. + """ + basepath, _ = os.path.splitext(os.path.basename(path)) + for line in block: + line = line.replace("###FILENAME###", path) + line = line.replace("###BASENAME###", basepath) + outfile.write(line) + + +def match_globs(globs): + """List all files matching any item in globs. + + Eliminates duplicates. + """ + return sorted({ + path + for pattern in globs + for path in glob(pattern) + }) + + +def expand_foreach(globs, block, outfile): + """Expand a foreach block for each file matching one of globs. + + Write the results to outfile. + """ + # We'll be iterating over block a variable number of times. Turn it + # from a generic iterable into an immutable array. + block = tuple(block) + for path in match_globs(globs): + expand_foreach_file(path, block, outfile) + + +# Header to be prefixed to the generated file. +OUTPUT_HEADER = dedent("""\ + # AUTOMATICALLY GENERATED FILE -- DO NOT EDIT. + # + # This file is generated automatically by libpqxx's {script} script, and + # will be rewritten from time to time. + # + # If you modify this file, chances are your modifications will be lost. + # + # The {script} script should be available in the tools directory of the + # libpqxx source archive. + """) + + +foreach_marker = r"###MAKTEMPLATE:FOREACH " +end_foreach_marker = r"###MAKTEMPLATE:ENDFOREACH" + + +def parse_foreach(line): + """Parse FOREACH directive, if line contains one. + + :param line: One line of template input. + :return: A list of FOREACH globs, or None if this was not a FOREACH line. + """ + line = line.strip() + if line.startswith(foreach_marker): + return line[len(foreach_marker):].split(',') + else: + return None + + +def read_foreach_block(infile): + """Read a FOREACH block from infile (not including the FOREACH directive). + + Assumes that the FOREACH directive was in the preceding line. Consumes + the line with the ENDFOREACH directive, but does not yield it. + + :return: Iterable of lines. + """ + for line in infile: + if line.strip().startswith(end_foreach_marker): + return + yield line + + +def expand_template(infile, outfile): + """Expand the template in infile, and write the results to outfile.""" + for line in infile: + globs = parse_foreach(line) + if globs is None: + # Not a FOREACH line. Copy to output. + outfile.write(line) + else: + block = read_foreach_block(infile) + expand_foreach(globs, block, outfile) + + +@contextmanager +def open_stream(path=None, default=None, mode='r'): + """Open file at given path, or yield default. Close as appropriate. + + The default should be a stream, not a path; closing the context will not + close it. + """ + if path is None: + yield default + else: + with open(path, mode) as stream: + yield stream + + +def parse_args(): + """Parse command-line arguments. + + :return: Tuple of: input path (or None for stdin), output path (or None + for stdout). + """ + parser = ArgumentParser( + description=__doc__, formatter_class=RawDescriptionHelpFormatter) + + parser.add_argument( + 'template', nargs='?', + help="Input template. Defaults to standard input.") + parser.add_argument( + 'output', nargs='?', + help="Output file. Defaults to standard output.") + + args = parser.parse_args() + return args.template, args.output + + +def write_header(stream, template_path=None): + """Write header to stream.""" + hr = ('# ' + '#' * 78) + "\n" + script = os.path.basename(argv[0]) + + outstream.write(hr) + outstream.write(OUTPUT_HEADER.format(script=script)) + if template_path is not None: + outstream.write("#\n") + outstream.write("# Generated from template '%s'.\n" % template_path) + outstream.write(hr) + + +if __name__ == '__main__': + try: + template_path, output_path = parse_args() + except ArgumentError as error: + stderr.write('%s\n' % error) + sys.exit(2) + + input_stream = open_stream(template_path, stdin, 'r') + output_stream = open_stream(output_path, stdout, 'w') + with input_stream as instream, output_stream as outstream: + write_header(outstream, template_path) + expand_template(instream, outstream) diff --git a/ext/libpqxx-7.7.3/tools/test_all.py b/ext/libpqxx-7.7.3/tools/test_all.py new file mode 100755 index 000000000..29f33050c --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/test_all.py @@ -0,0 +1,630 @@ +#! /usr/bin/env python3 +"""Brute-force test script: test libpqxx against many compilers etc. + +This script makes no changes in the source tree; all builds happen in +temporary directories. + +To make this possible, you may need to run "make distclean" in the +source tree. The configure script will refuse to configure otherwise. +""" + +# Without this, pocketlint does not yet understand the print function. +from __future__ import print_function + +from abc import ( + ABCMeta, + abstractmethod, + ) +from argparse import ArgumentParser +from contextlib import contextmanager +from datetime import datetime +from functools import partial +import json +from multiprocessing import ( + JoinableQueue, + Process, + Queue, + ) +from multiprocessing.pool import ( + Pool, + ) +from os import ( + cpu_count, + getcwd, + ) +import os.path +from queue import Empty +from shutil import rmtree +from subprocess import ( + CalledProcessError, + check_call, + check_output, + DEVNULL, + ) +from sys import ( + stderr, + stdout, + ) +from tempfile import mkdtemp +from textwrap import dedent + + +CPUS = cpu_count() + +GCC_VERSIONS = list(range(8, 14)) +GCC = ['g++-%d' % ver for ver in GCC_VERSIONS] +CLANG_VERSIONS = list(range(7, 15)) +CLANG = ['clang++-6.0'] + ['clang++-%d' % ver for ver in CLANG_VERSIONS] +CXX = GCC + CLANG + +STDLIB = ( + '', + '-stdlib=libc++', + ) + +OPT = ('-O0', '-O3') + +LINK = { + 'static': ['--enable-static', '--disable-shared'], + 'dynamic': ['--disable-static', '--enable-shared'], +} + +DEBUG = { + 'plain': [], + 'audit': ['--enable-audit'], + 'maintainer': ['--enable-maintainer-mode'], + 'full': ['--enable-audit', '--enable-maintainer-mode'], +} + + +# CMake "generators." Maps a value for cmake's -G option to a command line to +# run. +# +# I prefer Ninja if available, because it's fast. But hey, the default will +# work. +# +# Maps the name of the generator (as used with cmake's -G option) to the +# actual command line needed to do the build. +CMAKE_GENERATORS = { + 'Ninja': ['ninja'], + 'Unix Makefiles': ['make', '-j%d' % CPUS], +} + + +class Fail(Exception): + """A known, well-handled exception. Doesn't need a traceback.""" + + +class Skip(Exception): + """"We're not doing this build. It's not an error though.""" + + +def run(cmd, output, cwd=None): + """Run a command, write output to file-like object.""" + command_line = ' '.join(cmd) + output.write("%s\n\n" % command_line) + check_call(cmd, stdout=output, stderr=output, cwd=cwd) + + +def report(output, message): + """Report a message to output, and standard output.""" + print(message, flush=True) + output.write('\n\n') + output.write(message) + output.write('\n') + + +def file_contains(path, text): + """Does the file at path contain text?""" + with open(path) as stream: + for line in stream: + if text in line: + return True + return False + + +@contextmanager +def tmp_dir(): + """Create a temporary directory, and clean it up again.""" + tmp = mkdtemp() + try: + yield tmp + finally: + rmtree(tmp) + + +def write_check_code(work_dir): + """Write a simple C++ program so we can tesst whether we can compile it. + + Returns the file's full path. + """ + path = os.path.join(work_dir, "check.cxx") + with open(path, 'w') as source: + source.write(dedent("""\ + #include + int main() + { + std::cout << "Hello world." << std::endl; + } + """)) + + return path + + +def check_compiler(work_dir, cxx, stdlib, check, verbose=False): + """Is the given compiler combo available?""" + err_file = os.path.join(work_dir, 'stderr.log') + if verbose: + err_output = open(err_file, 'w') + else: + err_output = DEVNULL + try: + command = [cxx, check] + if stdlib != '': + command.append(stdlib) + check_call(command, cwd=work_dir, stderr=err_output) + except (OSError, CalledProcessError): + if verbose: + with open(err_file) as errors: + stdout.write(errors.read()) + print("Can't build with '%s %s'. Skipping." % (cxx, stdlib)) + return False + else: + return True + + +# TODO: Use Pool. +def check_compilers(compilers, stdlibs, verbose=False): + """Check which compiler configurations are viable.""" + with tmp_dir() as work_dir: + check = write_check_code(work_dir) + return [ + (cxx, stdlib) + for stdlib in stdlibs + for cxx in compilers + if check_compiler( + work_dir, cxx, stdlib, check=check, verbose=verbose) + ] + + +def find_cmake_command(): + """Figure out a CMake generator we can use, or None.""" + try: + caps = check_output(['cmake', '-E', 'capabilities']) + except FileNotFoundError: + return None + + names = {generator['name'] for generator in json.loads(caps)['generators']} + for gen in CMAKE_GENERATORS.keys(): + if gen in names: + return gen + return None + + +class Config: + """Configuration for a build. + + These classes must be suitable for pickling, so we can send its objects to + worker processes. + """ + __metaclass__ = ABCMeta + + @abstractmethod + def name(self): + """Return an identifier for this build configuration.""" + + def make_log_name(self): + """Compose log file name for this build.""" + return "build-%s.out" % self.name() + + +class Build: + """A pending or ondoing build, in its own directory. + + Each step returns True for Success, or False for failure. + + These classes must be suitable for pickling, so we can send its objects to + worker processes. + """ + def __init__(self, logs_dir, config=None): + self.config = config + self.log = os.path.join(logs_dir, config.make_log_name()) + # Start a fresh log file. + with open(self.log, 'w') as log: + log.write("Starting %s.\n" % datetime.utcnow()) + self.work_dir = mkdtemp() + + def clean_up(self): + """Delete the build tree.""" + rmtree(self.work_dir) + + @abstractmethod + def configure(self, log): + """Prepare for a build.""" + + @abstractmethod + def build(self, log): + """Build the code, including the tests. Don't run tests though.""" + + def test(self, log): + """Run tests.""" + run( + [os.path.join(os.path.curdir, 'test', 'runner')], log, + cwd=self.work_dir) + + def logging(self, function): + """Call function, pass open write handle for `self.log`.""" +# TODO: Should probably be a decorator. + with open(self.log, 'a') as log: + try: + function(log) + except Exception as error: + log.write("%s\n" % error) + raise + + def do_configure(self): + """Call `configure`, writing output to `self.log`.""" + self.logging(self.configure) + + def do_build(self): + """Call `build`, writing output to `self.log`.""" + self.logging(self.build) + + def do_test(self): + """Call `test`, writing output to `self.log`.""" + self.logging(self.test) + + +class AutotoolsConfig(Config): + """A combination of build options for the "configure" script.""" + def __init__(self, cxx, opt, stdlib, link, link_opts, debug, debug_opts): + self.cxx = cxx + self.opt = opt + self.stdlib = stdlib + self.link = link + self.link_opts = link_opts + self.debug = debug + self.debug_opts = debug_opts + + def name(self): + return '_'.join([ + self.cxx, self.opt, self.stdlib, self.link, self.debug]) + + +class AutotoolsBuild(Build): + """Build using the "configure" script.""" + __metaclass__ = ABCMeta + + def configure(self, log): + configure = [ + os.path.join(getcwd(), "configure"), + "CXX=%s" % self.config.cxx, + ] + + if self.config.stdlib == '': + configure += [ + "CXXFLAGS=%s" % self.config.opt, + ] + else: + configure += [ + "CXXFLAGS=%s %s" % (self.config.opt, self.config.stdlib), + "LDFLAGS=%s" % self.config.stdlib, + ] + + configure += [ + "--disable-documentation", + ] + self.config.link_opts + self.config.debug_opts + + run(configure, log, cwd=self.work_dir) + + def build(self, log): + run(['make', '-j%d' % CPUS], log, cwd=self.work_dir) + # Passing "TESTS=" like this will suppress the actual running of + # the tests. We run them in the "test" stage. + run(['make', '-j%d' % CPUS, 'check', 'TESTS='], log, cwd=self.work_dir) + + +class CMakeConfig(Config): + """Configuration for a CMake build.""" + def __init__(self, generator): + self.generator = generator + self.builder = CMAKE_GENERATORS[generator] + + def name(self): + return "cmake" + + +class CMakeBuild(Build): + """Build using CMake. + + Ignores the config for now. + """ + __metaclass__ = ABCMeta + + def configure(self, log): + source_dir = getcwd() + generator = self.config.generator + run( + ['cmake', '-G', generator, source_dir], output=log, + cwd=self.work_dir) + + def build(self, log): + run(self.config.builder, log, cwd=self.work_dir) + + +def parse_args(): + """Parse command-line arguments.""" + parser = ArgumentParser(description=__doc__) + parser.add_argument('--verbose', '-v', action='store_true') + parser.add_argument( + '--compilers', '-c', default=','.join(CXX), + help="Compilers, separated by commas. Default is %(default)s.") + parser.add_argument( + '--optimize', '-O', default=','.join(OPT), + help=( + "Alternative optimisation options, separated by commas. " + "Default is %(default)s.")) + parser.add_argument( + '--stdlibs', '-L', default=','.join(STDLIB), + help=( + "Comma-separated options for choosing standard library. " + "Defaults to %(default)s.")) + parser.add_argument( + '--logs', '-l', default='.', metavar='DIRECTORY', + help="Write build logs to DIRECTORY.") + parser.add_argument( + '--jobs', '-j', default=CPUS, metavar='CPUS', + help=( + "When running 'make', run up to CPUS concurrent processes. " + "Defaults to %(default)s.")) + parser.add_argument( + '--minimal', '-m', action='store_true', + help="Make it as short a run as possible. For testing this script.") + return parser.parse_args() + + +def soft_get(queue, block=True): + """Get an item off `queue`, or `None` if the queue is empty.""" + try: + return queue.get(block) + except Empty: + return None + + +def read_queue(queue, block=True): + """Read entries off `queue`, terminating when it gets a `None`. + + Also terminates when the queue is empty. + """ + entry = soft_get(queue, block) + while entry is not None: + yield entry + entry = soft_get(queue, block) + + +def service_builds(in_queue, fail_queue, out_queue): + """Worker process for "build" stage: process one job at a time. + + Sends successful builds to `out_queue`, and failed builds to `fail_queue`. + + Terminates when it receives a `None`, at which point it will send a `None` + into `out_queue` in turn. + """ + for build in read_queue(in_queue): + try: + build.do_build() + except Exception as error: + fail_queue.put((build, "%s" % error)) + else: + out_queue.put(build) + in_queue.task_done() + + # Mark the end of the queue. + out_queue.put(None) + + +def service_tests(in_queue, fail_queue, out_queue): + """Worker process for "test" stage: test one build at a time. + + Sends successful builds to `out_queue`, and failed builds to `fail_queue`. + + Terminates when it receives a final `None`. Does not send out a final + `None` of its own. + """ + for build in read_queue(in_queue): + try: + build.do_test() + except Exception as error: + fail_queue.put((build, "%s" % error)) + else: + out_queue.put(build) + in_queue.task_done() + + +def report_failures(queue, message): + """Report failures from a failure queue. Return total number.""" + failures = 0 + for build, error in read_queue(queue, block=False): + print("%s: %s - %s" % (message, build.config.name(), error)) + failures += 1 + return failures + + +def count_entries(queue): + """Get and discard all entries from `queue`, return the total count.""" + total = 0 + for _ in read_queue(queue, block=False): + total += 1 + return total + + +def gather_builds(args): + """Produce the list of builds we want to perform.""" + if args.verbose: + print("\nChecking available compilers.") + + compiler_candidates = args.compilers.split(',') + compilers = check_compilers( + compiler_candidates, args.stdlibs.split(','), + verbose=args.verbose) + if list(compilers) == []: + raise Fail( + "Did not find any viable compilers. Tried: %s." + % ', '.join(compiler_candidates)) + + opt_levels = args.optimize.split(',') + link_types = LINK.items() + debug_mixes = DEBUG.items() + + if args.minimal: + compilers = compilers[:1] + opt_levels = opt_levels[:1] + link_types = list(link_types)[:1] + debug_mixes = list(debug_mixes)[:1] + + builds = [ + AutotoolsBuild( + args.logs, + AutotoolsConfig( + opt=opt, link=link, link_opts=link_opts, debug=debug, + debug_opts=debug_opts, cxx=cxx, stdlib=stdlib)) + for opt in sorted(opt_levels) + for link, link_opts in sorted(link_types) + for debug, debug_opts in sorted(debug_mixes) + for cxx, stdlib in compilers + ] + + cmake = find_cmake_command() + if cmake is not None: + builds.append(CMakeBuild(args.logs, CMakeConfig(cmake))) + return builds + + +def enqueue(queue, build, *args): + """Put `build` on `queue`. + + Ignores additional arguments, so that it can be used as a clalback for + `Pool`. + + We do this instead of a lambda in order to get the closure right. We want + the build for the current iteration, not the last one that was executed + before the lambda runs. + """ + queue.put(build) + + +def enqueue_error(queue, build, error): + """Put the pair of `build` and `error` on `queue`.""" + queue.put((build, error)) + + +def main(args): + """Do it all.""" + if not os.path.isdir(args.logs): + raise Fail("Logs location '%s' is not a directory." % args.logs) + + builds = gather_builds(args) + if args.verbose: + print("Lined up %d builds." % len(builds)) + + # The "configure" step is single-threaded. We can run many at the same + # time, even when we're also running a "build" step at the same time. + # This means we may run a lot more processes than we have CPUs, but there's + # no law against that. There's also I/O time to be covered. + configure_pool = Pool() + + # Builds which have failed the "configure" stage, with their errors. This + # queue must never stall, so that we can let results pile up here while the + # work continues. + configure_fails = Queue(len(builds)) + + # Waiting list for the "build" stage. It contains Build objects, + # terminated by a final None to signify that there are no more builds to be + # done. + build_queue = JoinableQueue(10) + + # Builds that have failed the "build" stage. + build_fails = Queue(len(builds)) + + # Waiting list for the "test" stage. It contains Build objects, terminated + # by a final None. + test_queue = JoinableQueue(10) + + # The "build" step tries to utilise all CPUs, and it may use a fair bit of + # memory. Run only one of these at a time, in a single worker process. + build_worker = Process( + target=service_builds, args=(build_queue, build_fails, test_queue)) + build_worker.start() + + # Builds that have failed the "test" stage. + test_fails = Queue(len(builds)) + + # Completed builds. This must never stall. + done_queue = JoinableQueue(len(builds)) + + # The "test" step can not run concurrently (yet). So, run tests serially + # in a single worker process. It takes its jobs directly from the "build" + # worker. + test_worker = Process( + target=service_tests, args=(test_queue, test_fails, done_queue)) + test_worker.start() + + # Feed all builds into the "configure" pool. Each build which passes this + # stage goes into the "build" queue. + for build in builds: + configure_pool.apply_async( + build.do_configure, callback=partial(enqueue, build_queue, build), + error_callback=partial(enqueue_error, configure_fails, build)) + if args.verbose: + print("All jobs are underway.") + configure_pool.close() + configure_pool.join() + +# TODO: Async reporting for faster feedback. + configure_fail_count = report_failures(configure_fails, "CONFIGURE FAIL") + if args.verbose: + print("Configure stage done.") + + # Mark the end of the build queue for the build worker. + build_queue.put(None) + + build_worker.join() +# TODO: Async reporting for faster feedback. + build_fail_count = report_failures(build_fails, "BUILD FAIL") + if args.verbose: + print("Build step done.") + + # Mark the end of the test queue for the test worker. + test_queue.put(None) + + test_worker.join() +# TODO: Async reporting for faster feedback. +# TODO: Collate failures into meaningful output, e.g. "shared library fails." + test_fail_count = report_failures(test_fails, "TEST FAIL") + if args.verbose: + print("Test step done.") + + # All done. Clean up. + for build in builds: + build.clean_up() + + ok_count = count_entries(done_queue) + if ok_count == len(builds): + print("All tests OK.") + else: + print( + "Failures during configure: %d - build: %d - test: %d. OK: %d." + % ( + configure_fail_count, + build_fail_count, + test_fail_count, + ok_count, + )) + + +if __name__ == '__main__': + try: + exit(main(parse_args())) + except Fail as failure: + stderr.write("%s\n" % failure) + exit(2) diff --git a/ext/libpqxx-7.7.3/tools/todo b/ext/libpqxx-7.7.3/tools/todo new file mode 100755 index 000000000..87429f4b2 --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/todo @@ -0,0 +1,31 @@ +#! /bin/bash +# +# List "TODO" and "XXX" items in the given files, or throughout the source +# code. + +set -e -u -o pipefail + +# TODO: Make location-independent? +find_source() { + echo configure.ac + find . -name \*.cxx -o -name \*.hxx | sed -e 's|^\./||' | sort + for f in $(ls tools) + do + echo tools/$f + done +} + + +FILES=${*:-$(find_source)} + + +# Search for "$1:" in files $2. +# (This function adds the colon. That way, the search statement itself won't +# show up in the search.) +search_for() { + grep $1: $2 +} + + +search_for XXX "$FILES" || true +search_for TODO "$FILES" || true diff --git a/ext/libpqxx-7.7.3/tools/update-copyright b/ext/libpqxx-7.7.3/tools/update-copyright new file mode 100755 index 000000000..9d1eb5a97 --- /dev/null +++ b/ext/libpqxx-7.7.3/tools/update-copyright @@ -0,0 +1,29 @@ +#! /bin/bash +# +# Update the libpqxx copyright strings in the current directory. +# +# Usage: update-copyright [year] +# +# Where "year" is the new copyright year. Defaults to the current year. +# +# Assumes GNU grep and GNU sed. +set -eu -o pipefail + +# The regexes are a bit awkward because they must work in both grep and sed. +# +# F'rinstance, PREFIX can't include the dash because our replacement string in +# sed would have a backreference (e.g. "\3") immediately followed by a year +# (e.g. 2022), and there's no clear boundary between the backreference number +# and the year: "\32022". +PREFIX='Copyright (c),* 2000' +YEAR='20[0-9][0-9]' +NEW_YEAR="${1:-$(date '+%Y')}" +SUFFIX=',* \(.* and \)*Jeroen T\. Vermeulen' +grep -rIl "$PREFIX-$YEAR$SUFFIX" | + xargs -r sed -i -e "s/\\($PREFIX\\)-$YEAR\\($SUFFIX\\)/\\1-$NEW_YEAR\\2/" + +# This one is so different that I'd rather keep it a special case. +sed \ + -i \ + -e "s/\\(2000\\)-$YEAR\\(,* Jeroen T\\. Vermeulen\\)/\1-$NEW_YEAR\\2/" \ + doc/conf.py diff --git a/ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/bug_report.md b/ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..3d4069bbd --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,26 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Minimal code to reproduce the bug. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Environment:** + - OS: [e.g. ubuntu] + - Compiler: [e.g. gcc 7.3.1, clang 3.9.1] + - hiredis version: [e.g. v1.0.0, master] + - redis-plus-plus version: [e.g. 1.3.2, master, commit b0a42e] + +**Additional context** +Add any other context about the problem here. diff --git a/ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/feature_request.md b/ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..e0c0168b2 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE]" +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/question.md b/ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 000000000..acaab024e --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,27 @@ +--- +name: Question +about: Ask a question +title: "[QUESTION]" +labels: '' +assignees: '' + +--- + +**Before Asking A Question** +The [README.md file](https://github.com/sewenew/redis-plus-plus/blob/master/README.md) has a detailed introduction with examples on how to use redis-plus-plus, and [redis.h](https://github.com/sewenew/redis-plus-plus/blob/master/src/sw/redis%2B%2B/redis.h) has doxgen style comments for most commands. Before asking a question, please try to find answers with these two files. + +For general questions on redis-plus-plus, you can ask questions at StackOverflow with [redis tag](https://stackoverflow.com/questions/tagged/redis), and normally, you'll get faster response on StackOverflow. + +However, if you still don't get answers, feel free to ask a question here. + +**Describe the problem** +A clear and concise description of the problem, with minimal code to reproduce the problem. + +**Environment:** + - OS: [e.g. ubuntu] + - Compiler: [e.g. gcc 7.3.1, clang 3.9.1] + - hiredis version: [e.g. v1.0.0, master] + - redis-plus-plus version: [e.g. 1.3.2, master, commit b0a42e] + +**Additional context** +Add any other context about the problem here. diff --git a/ext/redis-plus-plus-1.3.3/.gitignore b/ext/redis-plus-plus-1.3.3/.gitignore new file mode 100644 index 000000000..259148fa1 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/.gitignore @@ -0,0 +1,32 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app diff --git a/ext/redis-plus-plus-1.3.3/.travis.yml b/ext/redis-plus-plus-1.3.3/.travis.yml new file mode 100644 index 000000000..ddcbe3a3e --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/.travis.yml @@ -0,0 +1,32 @@ +language: cpp + +os: + - linux + - osx + +dist: bionic + +compiler: + - gcc + - clang + +before_install: + - wget -L https://github.com/redis/redis/archive/6.0.8.tar.gz -O redis-6.0.8.tar.gz + - tar xfz redis-6.0.8.tar.gz + - wget -L https://github.com/redis/hiredis/archive/master.zip -O hiredis-master.zip + - unzip hiredis-master.zip + - mkdir -p $TRAVIS_BUILD_DIR/install + +install: + - cd redis-6.0.8 && make -j2 && cd .. + - ./redis-6.0.8/src/redis-server & + - cd hiredis-master && make PREFIX=$TRAVIS_BUILD_DIR/install -j2 install && cd .. + +script: + - mkdir compile && cd compile && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$TRAVIS_BUILD_DIR/install .. && make -j2 && cd .. + - ./compile/test/test_redis++ -h 127.0.0.1 -p 6379 + +branches: + only: + - master + - dev diff --git a/ext/redis-plus-plus-1.3.3/CMakeLists.txt b/ext/redis-plus-plus-1.3.3/CMakeLists.txt new file mode 100644 index 000000000..29b8bfe32 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/CMakeLists.txt @@ -0,0 +1,307 @@ +cmake_minimum_required(VERSION 3.1) + +set(REDIS_PLUS_PLUS_VERSION "1.3.3") +message(STATUS "redis-plus-plus version: ${REDIS_PLUS_PLUS_VERSION}") + +project(redis++ LANGUAGES CXX VERSION ${REDIS_PLUS_PLUS_VERSION}) + +set(REDIS_PLUS_PLUS_DEFAULT_BUILD_TYPE "Release") +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE ${REDIS_PLUS_PLUS_DEFAULT_BUILD_TYPE} CACHE STRING "Set build type" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo" "MinSizeRel") +endif() +message(STATUS "redis-plus-plus build type: ${CMAKE_BUILD_TYPE}") + +set(REDIS_PLUS_PLUS_DEFAULT_CXX_STANDARD 17) +if(NOT REDIS_PLUS_PLUS_CXX_STANDARD) + set(REDIS_PLUS_PLUS_CXX_STANDARD ${REDIS_PLUS_PLUS_DEFAULT_CXX_STANDARD} CACHE STRING "Set CXX standard" FORCE) + set_property(CACHE REDIS_PLUS_PLUS_CXX_STANDARD PROPERTY STRINGS "11" "14" "17" "20") +endif() +message(STATUS "redis-plus-plus build with CXX standard: c++${REDIS_PLUS_PLUS_CXX_STANDARD}") + +if(NOT WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++${REDIS_PLUS_PLUS_CXX_STANDARD}") +else() + if(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++${REDIS_PLUS_PLUS_CXX_STANDARD}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++${REDIS_PLUS_PLUS_CXX_STANDARD}") + endif() +endif() + +if(REDIS_PLUS_PLUS_BUILD_ASYNC) + if(REDIS_PLUS_PLUS_BUILD_ASYNC STREQUAL "libuv") + message(STATUS "redis-plus-plus build async interface with libuv") + + # libuv dependency + find_path(REDIS_PLUS_PLUS_ASYNC_LIB_HEADER NAMES uv.h) + find_library(REDIS_PLUS_PLUS_ASYNC_LIB uv) + else() + message(FATAL_ERROR "invalid REDIS_PLUS_PLUS_BUILD_ASYNC") + endif() +endif() + +set(REDIS_PLUS_PLUS_SOURCE_DIR src/sw/redis++) + +set(REDIS_PLUS_PLUS_SOURCES + "${REDIS_PLUS_PLUS_SOURCE_DIR}/command.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/command_options.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/connection.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/connection_pool.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/crc16.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/errors.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/pipeline.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/redis.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/redis_cluster.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/reply.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/sentinel.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/shards.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/shards_pool.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/subscriber.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/transaction.cpp" +) + +if(REDIS_PLUS_PLUS_BUILD_ASYNC) + list(APPEND REDIS_PLUS_PLUS_SOURCES + "${REDIS_PLUS_PLUS_SOURCE_DIR}/async_connection.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/async_connection_pool.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/async_redis.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/event_loop.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/async_sentinel.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/async_redis_cluster.cpp" + "${REDIS_PLUS_PLUS_SOURCE_DIR}/async_shards_pool.cpp" + ) + + if(NOT REDIS_PLUS_PLUS_ASYNC_FUTURE) + set(REDIS_PLUS_PLUS_ASYNC_FUTURE "std") + endif() + + if(REDIS_PLUS_PLUS_ASYNC_FUTURE STREQUAL "std") + set(REDIS_PLUS_PLUS_ASYNC_FUTURE_HEADER "${REDIS_PLUS_PLUS_SOURCE_DIR}/future/std") + elseif(REDIS_PLUS_PLUS_ASYNC_FUTURE STREQUAL "boost") + set(REDIS_PLUS_PLUS_ASYNC_FUTURE_HEADER "${REDIS_PLUS_PLUS_SOURCE_DIR}/future/boost") + find_package(Boost REQUIRED COMPONENTS system thread) + else() + message(FATAL_ERROR "invalid REDIS_PLUS_PLUS_ASYNC_FUTURE") + endif() +endif() + +# cxx utils +if(REDIS_PLUS_PLUS_CXX_STANDARD LESS 17) + set(CXX_UTILS_DIR "${REDIS_PLUS_PLUS_SOURCE_DIR}/cxx11") +else() + set(CXX_UTILS_DIR "${REDIS_PLUS_PLUS_SOURCE_DIR}/cxx17") +endif() + +# TLS support +option(REDIS_PLUS_PLUS_USE_TLS "Build with TLS support" OFF) +message(STATUS "redis-plus-plus TLS support: ${REDIS_PLUS_PLUS_USE_TLS}") + +if(REDIS_PLUS_PLUS_USE_TLS) + set(TLS_SUB_DIR "${REDIS_PLUS_PLUS_SOURCE_DIR}/tls") + + list(APPEND REDIS_PLUS_PLUS_SOURCES "${TLS_SUB_DIR}/tls.cpp") + + set(REDIS_PLUS_PLUS_DEPENDS "hiredis,hiredis_ssl") +else() + set(TLS_SUB_DIR "${REDIS_PLUS_PLUS_SOURCE_DIR}/no_tls") + + set(REDIS_PLUS_PLUS_DEPENDS "hiredis") +endif() + +# hiredis dependency +find_package(hiredis QUIET) +if(hiredis_FOUND) + list(APPEND REDIS_PLUS_PLUS_HIREDIS_LIBS hiredis::hiredis) + + if(REDIS_PLUS_PLUS_USE_TLS) + find_package(hiredis_ssl REQUIRED) + list(APPEND REDIS_PLUS_PLUS_HIREDIS_LIBS hiredis::hiredis_ssl) + endif() +else() + find_path(HIREDIS_HEADER hiredis) + find_library(HIREDIS_LIB hiredis) + list(APPEND REDIS_PLUS_PLUS_HIREDIS_LIBS ${HIREDIS_LIB}) + + if(REDIS_PLUS_PLUS_USE_TLS) + find_library(HIREDIS_TLS_LIB hiredis_ssl) + list(APPEND REDIS_PLUS_PLUS_HIREDIS_LIBS ${HIREDIS_TLS_LIB}) + endif() +endif() + +# Build static library +option(REDIS_PLUS_PLUS_BUILD_STATIC "Build static library" ON) +message(STATUS "redis-plus-plus build static library: ${REDIS_PLUS_PLUS_BUILD_STATIC}") + +if(REDIS_PLUS_PLUS_BUILD_STATIC) + set(STATIC_LIB redis++_static) + + add_library(${STATIC_LIB} STATIC ${REDIS_PLUS_PLUS_SOURCES}) + add_library(redis++::${STATIC_LIB} ALIAS ${STATIC_LIB}) + + list(APPEND REDIS_PLUS_PLUS_TARGETS ${STATIC_LIB}) + + target_include_directories(${STATIC_LIB} PUBLIC + $ + $ + $ + $) + + if(hiredis_FOUND) + target_link_libraries(${STATIC_LIB} PUBLIC ${REDIS_PLUS_PLUS_HIREDIS_LIBS}) + else() + target_include_directories(${STATIC_LIB} PUBLIC $) + endif() + + if(REDIS_PLUS_PLUS_BUILD_ASYNC) + target_include_directories(${STATIC_LIB} PUBLIC $) + target_include_directories(${STATIC_LIB} PUBLIC $) + if(REDIS_PLUS_PLUS_ASYNC_FUTURE STREQUAL "boost") + target_include_directories(${STATIC_LIB} SYSTEM PUBLIC $) + endif() + endif() + + if (WIN32) + target_compile_definitions(${STATIC_LIB} PRIVATE NOMINMAX) + set_target_properties(${STATIC_LIB} PROPERTIES CXX_STANDARD ${REDIS_PLUS_PLUS_CXX_STANDARD}) + set_target_properties(${STATIC_LIB} PROPERTIES OUTPUT_NAME redis++_static) + else() + target_compile_options(${STATIC_LIB} PRIVATE "-Wall" "-W" "-Werror") + set_target_properties(${STATIC_LIB} PROPERTIES OUTPUT_NAME redis++) + endif() + + set_target_properties(${STATIC_LIB} PROPERTIES CLEAN_DIRECT_OUTPUT 1) + set_target_properties(${STATIC_LIB} PROPERTIES CXX_EXTENSIONS OFF) + + option(REDIS_PLUS_PLUS_BUILD_STATIC_WITH_PIC "Build static library with position independent code" ON) + message(STATUS "redis-plus-plus build static library with position independent code: ${REDIS_PLUS_PLUS_BUILD_STATIC_WITH_PIC}") + + if(REDIS_PLUS_PLUS_BUILD_STATIC_WITH_PIC) + set_target_properties(${STATIC_LIB} PROPERTIES POSITION_INDEPENDENT_CODE ON) + endif() +endif() + +# Build shared library +option(REDIS_PLUS_PLUS_BUILD_SHARED "Build shared library" ON) +message(STATUS "redis-plus-plus build shared library: ${REDIS_PLUS_PLUS_BUILD_SHARED}") + +if(REDIS_PLUS_PLUS_BUILD_SHARED) + set(SHARED_LIB redis++) + + add_library(${SHARED_LIB} SHARED ${REDIS_PLUS_PLUS_SOURCES}) + add_library(redis++::${SHARED_LIB} ALIAS ${SHARED_LIB}) + + list(APPEND REDIS_PLUS_PLUS_TARGETS ${SHARED_LIB}) + + target_include_directories(${SHARED_LIB} PUBLIC + $ + $ + $ + $) + + if(hiredis_FOUND) + target_link_libraries(${SHARED_LIB} PUBLIC ${REDIS_PLUS_PLUS_HIREDIS_LIBS}) + else() + target_include_directories(${SHARED_LIB} PUBLIC $) + target_link_libraries(${SHARED_LIB} PUBLIC ${REDIS_PLUS_PLUS_HIREDIS_LIBS}) + endif() + + if(REDIS_PLUS_PLUS_BUILD_ASYNC) + target_include_directories(${SHARED_LIB} PUBLIC $) + target_include_directories(${SHARED_LIB} PUBLIC $) + target_link_libraries(${SHARED_LIB} PUBLIC ${REDIS_PLUS_PLUS_ASYNC_LIB}) + if(REDIS_PLUS_PLUS_ASYNC_FUTURE STREQUAL "boost") + target_include_directories(${SHARED_LIB} SYSTEM PUBLIC $) + endif() + endif() + + if(WIN32) + target_compile_definitions(${SHARED_LIB} PRIVATE NOMINMAX) + set_target_properties(${SHARED_LIB} PROPERTIES CXX_STANDARD ${REDIS_PLUS_PLUS_CXX_STANDARD}) + set_target_properties(${SHARED_LIB} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) + else() + target_compile_options(${SHARED_LIB} PRIVATE "-Wall" "-W" "-Werror") + endif() + + set_target_properties(${SHARED_LIB} PROPERTIES OUTPUT_NAME redis++) + set_target_properties(${SHARED_LIB} PROPERTIES CLEAN_DIRECT_OUTPUT 1) + set_target_properties(${SHARED_LIB} PROPERTIES CXX_EXTENSIONS OFF) + set_target_properties(${SHARED_LIB} PROPERTIES POSITION_INDEPENDENT_CODE ON) + set_target_properties(${SHARED_LIB} PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) +endif() + +option(REDIS_PLUS_PLUS_BUILD_TEST "Build tests for redis++" ON) +message(STATUS "redis-plus-plus build test: ${REDIS_PLUS_PLUS_BUILD_TEST}") + +if(REDIS_PLUS_PLUS_BUILD_TEST) + add_subdirectory(test) +endif() + +install(TARGETS ${REDIS_PLUS_PLUS_TARGETS} + EXPORT redis++-targets + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin + INCLUDES DESTINATION include) + +set(REDIS_PLUS_PLUS_CMAKE_DESTINATION share/cmake/redis++) + +install(EXPORT redis++-targets + FILE redis++-targets.cmake + NAMESPACE redis++:: + DESTINATION ${REDIS_PLUS_PLUS_CMAKE_DESTINATION}) + +# Install headers. +set(HEADER_PATH "sw/redis++") +file(GLOB HEADERS "${REDIS_PLUS_PLUS_SOURCE_DIR}/*.h*" "${TLS_SUB_DIR}/*.h" "${CXX_UTILS_DIR}/*.h" "${REDIS_PLUS_PLUS_ASYNC_FUTURE_HEADER}/*.h") +if(NOT REDIS_PLUS_PLUS_BUILD_ASYNC) + file(GLOB ASYNC_HEADERS "${REDIS_PLUS_PLUS_SOURCE_DIR}/async_*.h" "${REDIS_PLUS_PLUS_SOURCE_DIR}/event_*.h") + list(REMOVE_ITEM HEADERS ${ASYNC_HEADERS}) +endif() +install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${HEADER_PATH}) + +include(CMakePackageConfigHelpers) + +write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/cmake/redis++-config-version.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion) + +configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/redis++-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/redis++-config.cmake" + INSTALL_DESTINATION ${REDIS_PLUS_PLUS_CMAKE_DESTINATION}) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/redis++-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/redis++-config-version.cmake" + DESTINATION ${REDIS_PLUS_PLUS_CMAKE_DESTINATION}) + +export(EXPORT redis++-targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/redis++-targets.cmake" + NAMESPACE redis++::) + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/redis++.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/redis++.pc" @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/redis++.pc" + DESTINATION "lib/pkgconfig") + +# All the Debian-specific cpack defines. +if(${CMAKE_VERSION} VERSION_GREATER 3.6) + SET(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS "ON") +endif() +if(NOT DEFINED CPACK_DEBIAN_PACKAGE_DEPENDS) + SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libstdc++6, libhiredis-dev") +endif() +SET(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) +SET(CPACK_DEBIAN_PACKAGE_VERSION "${REDIS_PLUS_PLUS_VERSION}") +SET(CPACK_DEBIAN_PACKAGE_SOURCE "https://github.com/sewenew/redis-plus-plus") +message(STATUS "Debian package name: ${CPACK_PACKAGE_FILE_NAME}.deb") + +# All the common cpack defines. +if(NOT DEFINED CPACK_PACKAGE_NAME) + SET(CPACK_PACKAGE_NAME "libredis++-dev") +endif() +SET(CPACK_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") +SET(CPACK_PACKAGE_DESCRIPTION "A pure C++ client for Redis, based on hiredis.") +SET(CPACK_PACKAGE_CONTACT "anonymous") +SET(CPACK_GENERATOR "DEB") +INCLUDE(CPack) diff --git a/ext/redis-plus-plus-1.3.3/LICENSE b/ext/redis-plus-plus-1.3.3/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ext/redis-plus-plus-1.3.3/README.md b/ext/redis-plus-plus-1.3.3/README.md new file mode 100644 index 000000000..79aee8cef --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/README.md @@ -0,0 +1,2604 @@ +# redis-plus-plus + +[![Build Status](https://travis-ci.org/sewenew/redis-plus-plus.svg?branch=master)](https://travis-ci.org/sewenew/redis-plus-plus) + +- [Overview](#overview) + - [Features](#features) + - [Branches](#branches) +- [Installation](#installation) + - [Install hiredis](#install-hiredis) + - [Install redis-plus-plus](#install-redis-plus-plus) + - [Run Tests (Optional)](#run-tests-optional) + - [Use redis-plus-plus In Your Project](#use-redis-plus-plus-in-your-project) +- [Getting Started](#getting-started) +- [API Reference](#api-reference) + - [Connection](#connection) + - [Send Command to Redis Server](#send-command-to-redis-server) + - [Exception](#exception) + - [Generic Command Interface](#generic-command-interface) + - [Publish/Subscribe](#publishsubscribe) + - [Pipeline](#pipeline) + - [Transaction](#transaction) + - [Redis Cluster](#redis-cluster) + - [Redis Sentinel](#redis-sentinel) + - [Redis Stream](#redis-stream) + - [Redis Modules](#redis-modules) + - [Async Interface](#async-interface) +- [Redis Recipes](#redis-recipes) + - [Redlock](#redlock) +- [Author](#author) + +## Overview + +This is a C++ client library for Redis. It's based on [hiredis](https://github.com/redis/hiredis), and is compatible with C++ 17, C++ 14, and C++ 11. + +**NOTE**: I'm not a native speaker. So if the documentation is unclear, please feel free to open an issue or pull request. I'll response ASAP. + +### Features +- Most commands for Redis. +- Connection pool. +- Redis scripting. +- Thread safe unless otherwise stated. +- Redis publish/subscribe. +- Redis pipeline. +- Redis transaction. +- Redis Cluster. +- Redis Sentinel. +- STL-like interfaces. +- Generic command interface. +- Redis Stream. +- Redlock. +- Redis ACL. +- TLS/SSL support. + +### Branches + +The master branch is the stable branch, which passes all tests. The dev branch is unstable. If you want to contribute, please create pull request on dev branch. + +## Installation + +### Install hiredis + +Since *redis-plus-plus* is based on *hiredis*, you should install *hiredis* first. The minimum version requirement for *hiredis* is **v0.12.1**. However, [the latest stable release](https://github.com/redis/hiredis/releases) of *hiredis* is always recommended. + +**NOTE**: You must ensure that there's only 1 version of hiredis is installed. Otherwise, you might get some wired problems. Check the following issues for example: [issue 135](https://github.com/sewenew/redis-plus-plus/issues/135), [issue 140](https://github.com/sewenew/redis-plus-plus/issues/140) and [issue 158](https://github.com/sewenew/redis-plus-plus/issues/158). + +Normally, you can install *hiredis* with a C++ package manager, and that's the easiest way to do it, e.g. `sudo apt-get install libhiredis-dev`. However, if you want to install the latest code of hiredis, or a specified version (e.g. async support needs hiredis v1.0.0 or later), you can install it from source. + +Note again: DO NOT INSTALL MULTIPLE VERSIONS OF HIREDIS. + +``` +git clone https://github.com/redis/hiredis.git + +cd hiredis + +make + +make install +``` + +By default, *hiredis* is installed at */usr/local*. If you want to install *hiredis* at non-default location, use the following commands to specify the installation path. + +``` +make PREFIX=/non/default/path + +make PREFIX=/non/default/path install +``` + +### Install redis-plus-plus + +*redis-plus-plus* is built with [CMAKE](https://cmake.org). + +``` +git clone https://github.com/sewenew/redis-plus-plus.git + +cd redis-plus-plus + +mkdir build + +cd build + +cmake -DREDIS_PLUS_PLUS_CXX_STANDARD=17 .. + +make + +make install + +cd .. +``` + +If *hiredis* is installed at non-default location, you should use `CMAKE_PREFIX_PATH` to specify the installation path of *hiredis*. By default, *redis-plus-plus* is installed at */usr/local*. However, you can use `CMAKE_INSTALL_PREFIX` to install *redis-plus-plus* at non-default location. + +``` +cmake -DCMAKE_PREFIX_PATH=/path/to/hiredis -DCMAKE_INSTALL_PREFIX=/path/to/install/redis-plus-plus .. +``` + +Since version 1.3.0, by default, *redis-plus-plus* is built with the `-std=c++17` standard. So that we can use the [std::string_view](#stringview) and [std::optional](#optional) features. However, it can also be built with the `-std=c++11` or `-std=c++14` standard, and in that case, we have our own simple implementation of `std::string_view` and `std::optional`. In order to explicitly specify c++ standard, you can use the following cmake flag: `-DREDIS_PLUS_PLUS_CXX_STANDARD=11`. + +``` +cmake -DCMAKE_PREFIX_PATH=/path/to/hiredis -DCMAKE_INSTALL_PREFIX=/path/to/install/redis-plus-plus -DREDIS_PLUS_PLUS_CXX_STANDARD=11 .. +``` + +**NOTE**: You should build *redis-plus-plus* and your application with the same standard, e.g. if you build *redis-plus-plus* with C++17 standard, you MUST also build your application code with C++17 standard. + +When compiling *redis-plus-plus*, it also compiles a test program, which might take a while. However, you can disable building test with the following cmake option: `-DREDIS_PLUS_PLUS_BUILD_TEST=OFF`. + +``` +cmake -DCMAKE_PREFIX_PATH=/path/to/hiredis -DCMAKE_INSTALL_PREFIX=/path/to/install/redis-plus-plus -DREDIS_PLUS_PLUS_BUILD_TEST=OFF .. +``` + +By default, *redis-plus-plus* builds both a static library and a shared library. If you only want to build one of them, you can disable the other with `-DREDIS_PLUS_PLUS_BUILD_STATIC=OFF` or `-DREDIS_PLUS_PLUS_BUILD_SHARED=OFF`. + +*redis-plus-plus* builds static library with `-fPIC` option, i.e. Position Independent Code, by default. However, you can disable it with `-DREDIS_PLUS_PLUS_BUILD_STATIC_WITH_PIC=OFF`. + +#### Windows Support + +Now *hiredis* has Windows support, and since Visual Studio 2017, Visual Studio has built-in support for CMake. So *redis-plus-plus* also supports Windows platform now. It has been fully tested with Visual Studio 2017 and later on Win 10. I'm not familiar with Visual Studio environment, and the following doc might not be accurate. If you're familiar with the Windows platform, feel free to update this doc on how to install *redis-plus-plus* on Windows. + +##### CMake Support On Visual Studio + +The following are some links on how to build CMake project with Visual Studio 2017 or later. If you're not familiar with it, you'd better read these instructions first: + +- [CMake support in Visual Studio](https://devblogs.microsoft.com/cppblog/cmake-support-in-visual-studio/) +- [CMake projects in Visual Studio](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=vs-2017) +- [CMakeSettings.json schema reference](https://docs.microsoft.com/en-us/cpp/build/cmakesettings-reference?view=vs-2017) +- [Open a project from a GitHub repo](https://docs.microsoft.com/en-us/visualstudio/get-started/tutorial-open-project-from-repo?view=vs-2019#open-a-project-from-a-github-repo) + +**NOTE**: IMHO, Visual Studio 2017's support for CMake project is not very mature, and I recommend you to build *hiredis* and *redis-plus-plus with Visual Studio 2019. + +##### Build hiredis + +First of all, you need to get the latest code of *hiredis* on master branch. Older version might not support Windows platform. *hiredis*' CMakeLists.txt uses `add_compile_definitions` method, which is only supported by cmake 3.12 or later. However, Visual Studio 2017's cmake version is older than that. So if you're using Visual Studio 2017, you need to comment the following line in the CMakeLists.txt file: + +``` +#IF(WIN32) +# ADD_COMPILE_DEFINITIONS(_CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN) +#ENDIF() +``` + +You can use the **Open Folder** feature to open *hiredis* project, and build it with the instructions (links) mentioned above. + +##### Build redis-plus-plus + +Since *redis-plus-plus* depends on *hiredis*, we need to specify the installation paths of *hiredis* before building it. You can use the **Open Folder** feature to open *redis-plus-plus* project. You need to edit the *CMakeSetting.json* file (automatically generated by Visual Studio) to set *HIREDIS_HEADER*, *HIREDIS_LIB* and *TEST_HIREDIS_LIB* variables to specify the installation path of hiredis headers, installation path of hiredis dynamic library and installation path of hiredis static library. The following is an example of *CMakeSetting.json* file: + +``` +{ + "configurations": [ + { + "name": "x64-Release", + "generator": "Visual Studio 15 2017 Win64", + "configurationType": "Release", + "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-m -v:minimal", + "variables": [ + { + "name": "HIREDIS_HEADER", + "value": "installation path of hiredis header files", + "type": "PATH" + }, + { + "name": "HIREDIS_LIB", + "value": "installation path of dynamic library of hiredis", + "type": "FILEPATH" + }, + { + "name": "TEST_HIREDIS_LIB", + "value": "installation path of static library of hiredis", + "type": "FILEPATH" + } + ] + } + ] +} +``` + +Then you can build it the instructions (links) mentioned above. If you're building with Visual Studio 2017 in debug mode, you might get [/bigobj error](https://docs.microsoft.com/en-us/cpp/build/reference/bigobj-increase-number-of-sections-in-dot-obj-file?view=vs-2017) when building the test. In this case, you can disable building test by setting `-DREDIS_PLUS_PLUS_BUILD_TEST=OFF` or build it in Release mode. + +**NOTE**: + +- Since 1.3.0, *redis-puls-plus* is built with C++17 by default, and you should also set your application code to be built with C++17. If you still want to build the *redis-plus-plus* with C++11, you can set the `REDIS_PLUS_PLUS_CXX_STANDARD` cmake option to 11. +- TLS/SSL support has not been tested on Windows yet. + +##### The Order of Header Files + +On Windows platform, if your application code also needs to include *windows.h*. You must ensure that *sw/redis++/redis++.h* is included before *windows.h*. Check [this issue](https://github.com/sewenew/redis-plus-plus/issues/194) for detail. + +#### Building a redis-plus-plus Debian Package (Optional) + +Basic support for building a GNU/Debian package is supplied with the use of cmake. +The following example shows how to build the Debian package: + +``` +mkdir build; cd build +cmake .. +cpack -G DEB +``` + +The install prefix may be modified as follows: + +``` +mkdir build; cd build +cmake -DCMAKE_INSTALL_PREFIX=/usr .. +cpack -G DEB +``` + +### Run Tests (Optional) + +*redis-plus-plus* has been fully tested with the following compilers: + +``` +gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) +gcc version 5.5.0 20171010 (Ubuntu 5.5.0-12ubuntu1) +gcc version 6.5.0 20181026 (Ubuntu 6.5.0-2ubuntu1~18.04) +gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1) +gcc version 8.3.0 (Ubuntu 8.3.0-6ubuntu1~18.04.1) +gcc version 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2) +gcc version 10.2.1 20210110 (Debian 10.2.1-6) +clang version 3.9.1-19ubuntu1 (tags/RELEASE_391/rc2) +clang version 4.0.1-10 (tags/RELEASE_401/final) +clang version 5.0.1-4 (tags/RELEASE_501/final) +clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final) +clang version 7.0.0-3~ubuntu0.18.04.1 (tags/RELEASE_700/final) +clang version 8.0.1-3build1 (tags/RELEASE_801/final) +Apple clang version 11.0.0 (clang-1100.0.33.12) +Visual Studio 2017 (Win 10) +Visual Studio 2019 (Win 10) +``` + +If you build *redis-plus-plus* with `-DREDIS_PLUS_PLUS_BUILD_TEST=ON` (the default behavior, and you can disable building test with `-DREDIS_PLUS_PLUS_BUILD_TEST=OFF`), you'll get a test program in *compile/test* directory: *compile/test/test_redis++*. + +In order to run the tests, you need to set up a Redis instance, and a Redis Cluster. Since the test program will send most of Redis commands to the server and cluster, you need to set up Redis of the latest version (by now, it's 5.0). Otherwise, the tests might fail. For example, if you set up Redis 4.0 for testing, the test program will fail when it tries to send the `ZPOPMAX` command (a Redis 5.0 command) to the server. If you want to run the tests with other Redis versions, you have to comment out commands that haven't been supported by your Redis, from test source files in *redis-plus-plus/test/src/sw/redis++/* directory. Sorry for the inconvenience, and I'll fix this problem to make the test program work with any version of Redis in the future. + +**NOTE**: The latest version of Redis is only a requirement for running the tests. In fact, you can use *redis-plus-plus* with Redis of any version, e.g. Redis 2.0, Redis 3.0, Redis 4.0, Redis 5.0. + +**NEVER** run the test program in production envronment, since the keys, which the test program reads or writes, might conflict with your application. + +In order to run tests with both Redis and Redis Cluster, you can run the test program with the following command: + +``` +./compile/test/test_redis++ -h host -p port -a auth -n cluster_node -c cluster_port +``` + +- *host* and *port* are the host and port number of the Redis instance. +- *cluster_node* and *cluster_port* are the host and port number of Redis Cluster. You only need to set the host and port number of a single node in the cluster, *redis-plus-plus* will find other nodes automatically. +- *auth* is the password of the Redis instance and Redis Cluster. The Redis instance and Redis Cluster must be configured with the same password. If there's no password configured, don't set this option. + +If you only want to run tests with Redis, you only need to specify *host*, *port* and *auth* options: + +``` +./compile/test/test_redis++ -h host -p port -a auth +``` + +Similarly, if you only want to run tests with Redis Cluster, just specify *cluster_node*, *cluster_port* and *auth* options: + +``` +./compile/test/test_redis++ -a auth -n cluster_node -c cluster_port +``` + +By default, the test program will not test running *redis-plus-plus* in multi-threads environment. If you want to do multi-threads test, which might cost a long time, you can specify the *-m* option: + +``` +./compile/test/test_redis++ -h host -p port -a auth -n cluster_node -c cluster_port -m +``` + +If all tests have been passed, the test program will print the following message: + +``` +Pass all tests +``` + +Otherwise, it prints the error message. + +#### Performance + +*redis-plus-plus* runs as fast as *hiredis*, since it's a wrapper of *hiredis*. You can run *test_redis++* in benchmark mode to check the performance in your environment. + +``` +./compile/test/test_redis++ -h host -p port -a auth -n cluster_node -c cluster_port -b -t thread_num -s connection_pool_size -r request_num -k key_len -v val_len +``` + +- *-b* option turns the test program into benchmark mode. +- *thread_num* specifies the number of worker threads. `10` by default. +- *connection_pool_size* specifies the size of the connection pool. `5` by default. +- *request_num* specifies the total number of requests sent to server for each test. `100000` by default. +- *key_len* specifies the length of the key for each operation. `10` by default. +- *val_len* specifies the length of the value. `10` by default. + +The bechmark will generate `100` random binary keys for testing, and the size of these keys is specified by *key_len*. When the benchmark runs, it will read/write with these keys. So **NEVER** run the test program in your production environment, otherwise, it might inaccidently delete your data. + +### Use redis-plus-plus In Your Project + +After compiling the code, you'll get both shared library and static library. Since *redis-plus-plus* depends on *hiredis*, you need to link both libraries to your Application. Also don't forget to specify the c++ standard, `-std=c++17`, `-std=c++14` or `-std=c++11`, as well as the thread-related option. + +#### Use Static Libraries + +Take gcc as an example. + +``` +g++ -std=c++17 -o app app.cpp /path/to/libredis++.a /path/to/libhiredis.a -pthread +``` + +If *hiredis* and *redis-plus-plus* are installed at non-default location, you should use `-I` option to specify the header path. + +``` +g++ -std=c++17 -I/non-default/install/include/path -o app app.cpp /path/to/libredis++.a /path/to/libhiredis.a -pthread +``` + +#### Use Shared Libraries + +``` +g++ -std=c++17 -o app app.cpp -lredis++ -lhiredis -pthread +``` + +If *hiredis* and *redis-plus-plus* are installed at non-default location, you should use `-I` and `-L` options to specify the header and library paths. + +``` +g++ -std=c++17 -I/non-default/install/include/path -L/non-default/install/lib/path -o app app.cpp -lredis++ -lhiredis -pthread +``` + +When linking with shared libraries, and running your application, you might get the following error message: + +``` +error while loading shared libraries: xxx: cannot open shared object file: No such file or directory. +``` + +That's because the linker cannot find the shared libraries. In order to solve the problem, you can add the path where you installed *hiredis* and *redis-plus-plus* libraries, to `LD_LIBRARY_PATH` environment variable. For example: + +``` +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib +``` + +Check [this StackOverflow question](https://stackoverflow.com/questions/480764) for details on how to solve the problem. + +#### Build With Cmake + +If you're using cmake to build your application, you need to add *hiredis* and *redis-plus-plus* dependencies in your *CMakeLists.txt*: + +```CMake +# <---------- set c++ standard -------------> +# NOTE: you must build redis-plus-plus and your application code with the same standard. +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# <------------ add hiredis dependency ---------------> +find_path(HIREDIS_HEADER hiredis) +target_include_directories(target PUBLIC ${HIREDIS_HEADER}) + +find_library(HIREDIS_LIB hiredis) +target_link_libraries(target ${HIREDIS_LIB}) + +# <------------ add redis-plus-plus dependency --------------> +# NOTE: this should be *sw* NOT *redis++* +find_path(REDIS_PLUS_PLUS_HEADER sw) +target_include_directories(target PUBLIC ${REDIS_PLUS_PLUS_HEADER}) + +find_library(REDIS_PLUS_PLUS_LIB redis++) +target_link_libraries(target ${REDIS_PLUS_PLUS_LIB}) +``` + +See [this issue](https://github.com/sewenew/redis-plus-plus/issues/5) for a complete example of *CMakeLists.txt*. + +Also, if you installed *hiredis* and *redis-plus-plus* at non-default location, you need to run cmake with `CMAKE_PREFIX_PATH` option to specify the installation path of these two libraries. + +``` +cmake -DCMAKE_PREFIX_PATH=/installation/path/to/the/two/libs .. +``` + +## Getting Started + +```C++ +#include + +using namespace sw::redis; + +try { + // Create an Redis object, which is movable but NOT copyable. + auto redis = Redis("tcp://127.0.0.1:6379"); + + // ***** STRING commands ***** + + redis.set("key", "val"); + auto val = redis.get("key"); // val is of type OptionalString. See 'API Reference' section for details. + if (val) { + // Dereference val to get the returned value of std::string type. + std::cout << *val << std::endl; + } // else key doesn't exist. + + // ***** LIST commands ***** + + // std::vector to Redis LIST. + std::vector vec = {"a", "b", "c"}; + redis.rpush("list", vec.begin(), vec.end()); + + // std::initializer_list to Redis LIST. + redis.rpush("list", {"a", "b", "c"}); + + // Redis LIST to std::vector. + vec.clear(); + redis.lrange("list", 0, -1, std::back_inserter(vec)); + + // ***** HASH commands ***** + + redis.hset("hash", "field", "val"); + + // Another way to do the same job. + redis.hset("hash", std::make_pair("field", "val")); + + // std::unordered_map to Redis HASH. + std::unordered_map m = { + {"field1", "val1"}, + {"field2", "val2"} + }; + redis.hmset("hash", m.begin(), m.end()); + + // Redis HASH to std::unordered_map. + m.clear(); + redis.hgetall("hash", std::inserter(m, m.begin())); + + // Get value only. + // NOTE: since field might NOT exist, so we need to parse it to OptionalString. + std::vector vals; + redis.hmget("hash", {"field1", "field2"}, std::back_inserter(vals)); + + // ***** SET commands ***** + + redis.sadd("set", "m1"); + + // std::unordered_set to Redis SET. + std::unordered_set set = {"m2", "m3"}; + redis.sadd("set", set.begin(), set.end()); + + // std::initializer_list to Redis SET. + redis.sadd("set", {"m2", "m3"}); + + // Redis SET to std::unordered_set. + set.clear(); + redis.smembers("set", std::inserter(set, set.begin())); + + if (redis.sismember("set", "m1")) { + std::cout << "m1 exists" << std::endl; + } // else NOT exist. + + // ***** SORTED SET commands ***** + + redis.zadd("sorted_set", "m1", 1.3); + + // std::unordered_map to Redis SORTED SET. + std::unordered_map scores = { + {"m2", 2.3}, + {"m3", 4.5} + }; + redis.zadd("sorted_set", scores.begin(), scores.end()); + + // Redis SORTED SET to std::vector>. + // NOTE: The return results of zrangebyscore are ordered, if you save the results + // in to `std::unordered_map`, you'll lose the order. + std::vector> zset_result; + redis.zrangebyscore("sorted_set", + UnboundedInterval{}, // (-inf, +inf) + std::back_inserter(zset_result)); + + // Only get member names: + // pass an inserter of std::vector type as output parameter. + std::vector without_score; + redis.zrangebyscore("sorted_set", + BoundedInterval(1.5, 3.4, BoundType::CLOSED), // [1.5, 3.4] + std::back_inserter(without_score)); + + // Get both member names and scores: + // pass an back_inserter of std::vector> as output parameter. + std::vector> with_score; + redis.zrangebyscore("sorted_set", + BoundedInterval(1.5, 3.4, BoundType::LEFT_OPEN), // (1.5, 3.4] + std::back_inserter(with_score)); + + // ***** SCRIPTING commands ***** + + // Script returns a single element. + auto num = redis.eval("return 1", {}, {}); + + // Script returns an array of elements. + std::vector nums; + redis.eval("return {ARGV[1], ARGV[2]}", {}, {"1", "2"}, std::back_inserter(nums)); + + // mset with TTL + auto mset_with_ttl_script = R"( + local len = #KEYS + if (len == 0 or len + 1 ~= #ARGV) then return 0 end + local ttl = tonumber(ARGV[len + 1]) + if (not ttl or ttl <= 0) then return 0 end + for i = 1, len do redis.call("SET", KEYS[i], ARGV[i], "EX", ttl) end + return 1 + )"; + + // Set multiple key-value pairs with TTL of 60 seconds. + auto keys = {"key1", "key2", "key3"}; + std::vector args = {"val1", "val2", "val3", "60"}; + redis.eval(mset_with_ttl_script, keys.begin(), keys.end(), vals.begin(), vals.end()); + + // ***** Pipeline ***** + + // Create a pipeline. + auto pipe = redis.pipeline(); + + // Send mulitple commands and get all replies. + auto pipe_replies = pipe.set("key", "value") + .get("key") + .rename("key", "new-key") + .rpush("list", {"a", "b", "c"}) + .lrange("list", 0, -1) + .exec(); + + // Parse reply with reply type and index. + auto set_cmd_result = pipe_replies.get(0); + + auto get_cmd_result = pipe_replies.get(1); + + // rename command result + pipe_replies.get(2); + + auto rpush_cmd_result = pipe_replies.get(3); + + std::vector lrange_cmd_result; + pipe_replies.get(4, back_inserter(lrange_cmd_result)); + + // ***** Transaction ***** + + // Create a transaction. + auto tx = redis.transaction(); + + // Run multiple commands in a transaction, and get all replies. + auto tx_replies = tx.incr("num0") + .incr("num1") + .mget({"num0", "num1"}) + .exec(); + + // Parse reply with reply type and index. + auto incr_result0 = tx_replies.get(0); + + auto incr_result1 = tx_replies.get(1); + + std::vector mget_cmd_result; + tx_replies.get(2, back_inserter(mget_cmd_result)); + + // ***** Generic Command Interface ***** + + // There's no *Redis::client_getname* interface. + // But you can use *Redis::command* to get the client name. + val = redis.command("client", "getname"); + if (val) { + std::cout << *val << std::endl; + } + + // Same as above. + auto getname_cmd_str = {"client", "getname"}; + val = redis.command(getname_cmd_str.begin(), getname_cmd_str.end()); + + // There's no *Redis::sort* interface. + // But you can use *Redis::command* to send sort the list. + std::vector sorted_list; + redis.command("sort", "list", "ALPHA", std::back_inserter(sorted_list)); + + // Another *Redis::command* to do the same work. + auto sort_cmd_str = {"sort", "list", "ALPHA"}; + redis.command(sort_cmd_str.begin(), sort_cmd_str.end(), std::back_inserter(sorted_list)); + + // ***** Redis Cluster ***** + + // Create a RedisCluster object, which is movable but NOT copyable. + auto redis_cluster = RedisCluster("tcp://127.0.0.1:7000"); + + // RedisCluster has similar interfaces as Redis. + redis_cluster.set("key", "value"); + val = redis_cluster.get("key"); + if (val) { + std::cout << *val << std::endl; + } // else key doesn't exist. + + // Keys with hash-tag. + redis_cluster.set("key{tag}1", "val1"); + redis_cluster.set("key{tag}2", "val2"); + redis_cluster.set("key{tag}3", "val3"); + + std::vector hash_tag_res; + redis_cluster.mget({"key{tag}1", "key{tag}2", "key{tag}3"}, + std::back_inserter(hash_tag_res)); + +} catch (const Error &e) { + // Error handling. +} +``` + +## API Reference + +You can also see [redis.h](https://github.com/sewenew/redis-plus-plus/blob/master/src/sw/redis%2B%2B/redis.h) for doxygen style documentation. + +### Connection + +`Redis` class maintains a connection pool to Redis server. If the connection is broken, `Redis` reconnects to Redis server automatically. + +You can initialize a `Redis` instance with `ConnectionOptions` and `ConnectionPoolOptions`. `ConnectionOptions` specifies options for connection to Redis server, and `ConnectionPoolOptions` specifies options for conneciton pool. `ConnectionPoolOptions` is optional. If not specified, `Redis` maintains a single connection to Redis server. + +```C++ +ConnectionOptions connection_options; +connection_options.host = "127.0.0.1"; // Required. +connection_options.port = 6666; // Optional. The default port is 6379. +connection_options.password = "auth"; // Optional. No password by default. +connection_options.db = 1; // Optional. Use the 0th database by default. + +// Optional. Timeout before we successfully send request to or receive response from redis. +// By default, the timeout is 0ms, i.e. never timeout and block until we send or receive successfuly. +// NOTE: if any command is timed out, we throw a TimeoutError exception. +connection_options.socket_timeout = std::chrono::milliseconds(200); + +// Connect to Redis server with a single connection. +Redis redis1(connection_options); + +ConnectionPoolOptions pool_options; +pool_options.size = 3; // Pool size, i.e. max number of connections. + +// Optional. Max time to wait for a connection. 0ms by default, which means wait forever. +// Say, the pool size is 3, while 4 threds try to fetch the connection, one of them will be blocked. +pool_options.wait_timeout = std::chrono::milliseconds(100); + +// Optional. Max lifetime of a connection. 0ms by default, which means never expire the connection. +// If the connection has been created for a long time, i.e. more than `connection_lifetime`, +// it will be expired and reconnected. +pool_options.connection_lifetime = std::chrono::minutes(10); + +// Connect to Redis server with a connection pool. +Redis redis2(connection_options, pool_options); +``` + +**NOTE**: if you set `ConnectionOptions::socket_timeout`, and try to call blocking commands, e.g. `Redis::brpop`, `Redis::blpop`, `Redis::bzpopmax`, `Redis::bzpopmin`, you must ensure that `ConnectionOptions::socket_timeout` is larger than the timeout specified with these blocking commands. Otherwise, you might get `TimeoutError`, and lose message. + +See [ConnectionOptions](https://github.com/sewenew/redis-plus-plus/blob/master/src/sw/redis%2B%2B/connection.h#L40) and [ConnectionPoolOptions](https://github.com/sewenew/redis-plus-plus/blob/master/src/sw/redis%2B%2B/connection_pool.h#L30) for more options. Also see [issue 80](https://github.com/sewenew/redis-plus-plus/issues/80) for discussion on connection pool. + +**NOTE**: `Redis` class is movable but NOT copyable. + +```C++ +// auto redis3 = redis1; // this won't compile. + +// But it's movable. +auto redis3 = std::move(redis1); +``` + +*redis-plus-plus* also supports connecting to Redis server with Unix Domain Socket. + +```C++ +ConnectionOptions options; +options.type = ConnectionType::UNIX; +options.path = "/path/to/socket"; +Redis redis(options); +``` + +You can also connect to Redis server with a URI: + +``` +tcp://[[username:]password@]host[:port][/db] + +unix://[[username:]password@]path-to-unix-domain-socket[/db] +``` + +The *scheme* and *host* parts are required, and others are optional. If you're connecting to Redis with Unix Domain Socket, you should use the *unix* scheme, otherwise, you should use *tcp* scheme. The following is a list of default values for those optional parts: + +- username: *default* +- password: empty string, i.e. no password +- port: 6379 +- db: 0 + +**NOTE**: If your password or username contains '@', or your username contains ':', you cannot construct `Redis` object with URI. Because *redis-plus-plus* will incorrectly parse the URI. In this case, you need to use `ConnectionOptions` to construct `Redis` object. + +**NOTE**: [Redis 6.0 supports ACL](https://redis.io/topics/acl), and you can specify a username for the connection. However, before Redis 6.0, you cannot do that. + +Also, the following connection options can be specified with the query string of URI, e.g. *tcp://127.0.0.1?keep_alive=true&socket_timeout=100ms&connect_timeout=100ms*: + +- `ConnectionOptions::keep_alive`: *false* by default. +- `ConnectionOptions::socket_timeout`: *0ms* by default. +- `ConnectionOptions::connect_timeout`: *0ms* by default. + +**NOTE**: Options specified in query string are case-sensitive, i.e. all key-value pairs must be in lowercase. + +So far, you cannot specify connection pool options with URI, e.g. `ConnectionPoolOptions::size`. + +```C++ +// Single connection to the given host and port. +Redis redis1("tcp://127.0.0.1:6666"); + +// Use default port, i.e. 6379. +Redis redis2("tcp://127.0.0.1"); + +// Connect to Redis with password, and default port. +Redis redis3("tcp://pass@127.0.0.1"); + +// Connect to Redis and select the 2nd (db number starts from 0) database. +Redis redis4("tcp://127.0.0.1:6379/2"); + +// Set keep_alive option to true with query string. +Redis redis5("tcp://127.0.0.1:6379/2?keep_alive=true"); + +// Set socket_timeout to 50 milliseconds, and connect_timeout to 1 second with query string. +Redis redis6("tcp://127.0.0.1?socket_timeout=50ms&connect_timeout=1s"); + +// Connect to Unix Domain Socket. +Redis redis7("unix://path/to/socket"); +``` + +#### Lazily Create Connection + +Connections in the pool are lazily created. When the connection pool is initialized, i.e. the constructor of `Redis`, `Redis` does NOT connect to the server. Instead, it connects to the server only when you try to send command. In this way, we can avoid unnecessary connections. So if the pool size is 5, but the number of max concurrent connections is 3, there will be only 3 connections in the pool. + +#### Connection Failure + +You don't need to check whether `Redis` object connects to server successfully. If `Redis` fails to create a connection to Redis server, or the connection is broken at some time, it throws an exception of type `Error` when you try to send command with `Redis`. Even when you get an exception, i.e. the connection is broken, you don't need to create a new `Redis` object. You can reuse the `Redis` object to send commands, and the `Redis` object will try to reconnect to server automatically. If it reconnects successfully, it sends command to server. Otherwise, it throws an exception again. + +See the [Exception section](#exception) for details on exceptions. + +#### Reuse Redis object As Much As Possible + +It's NOT cheap to create a `Redis` object, since it will create new connections to Redis server. So you'd better reuse `Redis` object as much as possible. Also, it's safe to call `Redis`' member functions in multi-thread environment, and you can share `Redis` object in multiple threads. + +```C++ +// This is GOOD practice. +auto redis = Redis("tcp://127.0.0.1"); +for (auto idx = 0; idx < 100; ++idx) { + // Reuse the Redis object in the loop. + redis.set("key", "val"); +} + +// This is VERY BAD! It's very inefficient. +// NEVER DO IT!!! +for (auto idx = 0; idx < 100; ++idx) { + // Create a new Redis object for each iteration. + auto redis = Redis("tcp://127.0.0.1"); + redis.set("key", "val"); +} +``` + +#### TLS/SSL Support + +*redis-plus-plus* also has TLS support. However, in order to use this feature, you need to enable it when building *hiredis* and *redis-plus-plus*. + +**NOTE**: So far, TLS feature has not been tested on Windows platform. I'll fix it in the future. + +##### Enable TLS/SSL support + +When building *hiredis* with TLS support, you need to download *hiredis* of version *v1.0.0* or latter, and specify `USE_SSL=1` flag: + +``` +make PREFIX=/non/default/path USE_SSL=1 + +make PREFIX=/non/default/path USE_SSL=1 install +``` + +Then you can build *redis-plus-plus* to enable TLS support by specifying the `-DREDIS_PLUS_PLUS_USE_TLS=ON` option: + +``` +cmake -DREDIS_PLUS_PLUS_USE_TLS=ON .. +``` + +##### Connection Options + +In order to connect to Redis with TLS support, you need to specify the following connection options: + +``` +ConnectionOptions opts; +opts.host = "127.0.0.1"; +opts.port = 6379; + +opts.tls.enabled = true; // Required. `false` by default. +opts.tls.cert = "/path/to/client/certificate"; // Optional +opts.tls.key = "/path/to/private/key/file"; // Optional +opts.tls.cacert = "/path/to/CA/certificate/file"; // You can also set `opts.tls.cacertdir` instead. +opts.tls.sni = "server-name-indication"; // Optional +``` + +Although `tls.cert` and `tls.key` are optional, if you specify one of them, you must also specify the other. Instead of specifying `tls.cacert`, you can also specify `tls.cacertdir` to the directory where certificates are stored. + +These options are the same as `redis-cli`'s TLS related command line arguments, so you can also run `redis-cli --help` to get the detailed explanation of these options. + +Then you can use this `ConnectionOptions` to create a `Redis` object to connect to Redis server with TLS support. + +**NOTE**: When building your application code, you also need to link it with `libhiredis.a`, `libhiredis_ssl.a`, `libredis++.a` (or the corresponding shared libraries), `-lssl` and `-lcrypto`. + +##### Automatically Initialize OpenSSL Library + +By default, *redis-plus-plus* automatically initializes OpenSSL library, i.e. calls `SSL_library_init` and initializes locks if needed. However, your application code might already initialize OpenSSL library. In this case, you can call `tls::disable_auto_init()` to disable the initialization. You should call this function only once and call it before any other *redis-plus-plus* operation. Otherwise, the behavior is undefined. + +### Send Command to Redis Server + +You can send [Redis commands](https://redis.io/commands) through `Redis` object. `Redis` has one or more (overloaded) methods for each Redis command. The method has the same (lowercased) name as the corresponding command. For example, we have 3 overload methods for the `DEL key [key ...]` command: + +```C++ +// Delete a single key. +long long Redis::del(const StringView &key); + +// Delete a batch of keys: [first, last). +template +long long Redis::del(Input first, Input last); + +// Delete keys in the initializer_list. +template +long long Redis::del(std::initializer_list il); +``` + +With input parameters, these methods build a Redis command based on [Redis protocol](https://redis.io/topics/protocol), and send the command to Redis server. Then synchronously receive the reply, parse it, and return to the caller. + +Let's take a closer look at these methods' parameters and return values. + +#### Parameter Type + +Most of these methods have the same parameters as the corresponding commands. The following is a list of parameter types: + +| Parameter Type | Explaination | Example | Note | +| :------------: | ------------ | ------- | ---- | +| **StringView** | Parameters of string type. Normally used for key, value, member name, field name and so on | ***bool Redis::hset(const StringView &key, const StringView &field, const StringView &val)*** | See the [StringView section](#stringview) for details on `StringView` | +| **long long** | Parameters of integer type. Normally used for index (e.g. list commands) or integer | ***void ltrim(const StringView &key, long long start, long long stop)***
***long long decrby(const StringView &key, long long decrement)*** | | +| **double** | Parameters of floating-point type. Normally used for score (e.g. sorted set commands) or number of floating-point type | ***double incrbyfloat(const StringView &key, double increment)*** | | +| **std::chrono::duration**
**std::chrono::time_point** | Time-related parameters | ***bool expire(const StringView &key, const std::chrono::seconds &timeout)***
***bool expireat(const StringView &key, const std::chrono::time_point &tp)*** | | +| **std::pair** | Used for Redis hash's (field, value) pair | ***bool hset(const StringView &key, const std::pair &item)*** | | +| **std::pair** | Used for Redis geo's (longitude, latitude) pair | ***OptionalLongLong georadius(const StringView &key, const std::pair &location, double radius, GeoUnit unit, const StringView &destination, bool store_dist, long long count)*** | | +| **pair of iterators** | Use a pair of iterators to specify a range of input, so that we can pass the data in a STL container to these methods | ***template < typename Input >***
***long long del(Input first, Input last)*** | Throw an exception, if it's an empty range, i.e. *first == last* | +| **std::initializer_list< T >** | Use an initializer list to specify a batch of input | ***template < typename T >***
***long long del(std::initializer_list< T > il)*** | | +| **some options** | Options for some commands | ***UpdateType***, ***template < typename T > class BoundedInterval*** | See [command_options.h](https://github.com/sewenew/redis-plus-plus/blob/master/src/sw/redis%2B%2B/command_options.h) for details | + +##### StringView + +[std::string_view](http://en.cppreference.com/w/cpp/string/basic_string_view) is a good choice for read-only string parameter types. `std::string_view` was however only introduced in the C++ 17 standard, so if you build *redis-plus-plus* with the `-std=c++11` (i.e. by specifying `-DREDIS_PLUS_PLUS_CXX_STANDARD=11` with cmake command) or the `-std=c++14` standard, a [simple implementation](https://github.com/sewenew/redis-plus-plus/blob/master/src/sw/redis%2B%2B/cxx11/cxx_utils.h) of `std::string_view`, called `StringView`, is available. You could build *redis-plus-plus* with the `-std=c++17` standard (i.e. the default behavior), which will supply `std::string_view` natively. The `StringView` implementation will then be disregarded by aliasing it to `std::string_view`. This is done inside the *redis-plus-plus* library with: `using StringView = std::string_view`. + +Since there are conversions from `std::string` and c-style string to `StringView`, you can just pass `std::string` or c-style string to methods that need a `StringView` parameter. + +```C++ +// bool Redis::hset(const StringView &key, const StringView &field, const StringView &val) + +// Pass c-style string to StringView. +redis.hset("key", "field", "value"); + +// Pass std::string to StringView. +std::string key = "key"; +std::string field = "field"; +std::string val = "val"; +redis.hset(key, field, val); + +// Mix std::string and c-style string. +redis.hset(key, field, "value"); +``` + +#### Return Type + +[Redis protocol](https://redis.io/topics/protocol) defines 5 kinds of replies: +- *Status Reply*: Also known as *Simple String Reply*. It's a non-binary string reply. +- *Bulk String Reply*: Binary safe string reply. +- *Integer Reply*: Signed integer reply. Large enough to hold `long long`. +- *Array Reply*: (Nested) Array reply. +- *Error Reply*: Non-binary string reply that gives error info. + +Also these replies might be *NULL*. For instance, when you try to `GET` the value of a nonexistent key, Redis returns a *NULL Bulk String Reply*. + +As we mentioned above, replies are parsed into return values of these methods. The following is a list of return types: + +| Return Type | Explaination | Example | Note | +| :---------: | ------------ | ------- | ---- | +| **void** | *Status Reply* that should always return a string of "OK" | *RENAME*, *SETEX* | | +| **std::string** | *Status Reply* that NOT always return "OK", and *Bulk String Reply* | *PING*, *INFO* | | +| **bool** | *Integer Reply* that always returns 0 or 1 | *EXPIRE*, *HSET* | See the [Boolean Return Value section](#boolean-return-value) for the meaning of a boolean return value | +| **long long** | *Integer Reply* that not always return 0 or 1 | *DEL*, *APPEND* | | +| **double** | *Bulk String Reply* that represents a double | *INCRBYFLOAT*, *ZINCRBY* | | +| **std::pair** | *Array Reply* with exactly 2 elements. Since the return value is always an array of 2 elements, we return the 2 elements as a `std::pair`'s first and second elements | *BLPOP* | | +| **std::tuple** | *Array Reply* with fixed length and has more than 2 elements. Since length of the returned array is fixed, we return the array as a `std::tuple` | *BZPOPMAX* | | +| **output iterator** | General *Array Reply* with non-fixed/dynamic length. We use STL-like interface to return this kind of array replies, so that you can insert the return value into a STL container easily | *MGET*, *LRANGE* | Also, sometimes the type of output iterator decides which options to send with the command. See the [Examples section](#command-overloads) for details | +| **Optional< T >** | For any reply of type `T` that might be *NULL* | *GET*, *LPOP*, *BLPOP*, *BZPOPMAX* | See the [Optional section](#optional) for details on `Optional` | +| **Variant< Args... >** | For reply that might be of serval different types | *MEMORY STATS* | NOTE: so far, this type is only supported when compiling redis-plus-plus with C++ 17 standard. This is normally used with [generic command interface](https://github.com/sewenew/redis-plus-plus#generic-command-interface). See the [Variant section](#variant) for details on `Variant` | +| **STL container** | General *Array Reply* | *CONFIG GET* | Both *output iterator* and *STL container* are used for array reply. The difference is that *STL container* is normally used with [generic command interface](https://github.com/sewenew/redis-plus-plus#generic-command-interface). See the [STL container section](#stl-container) for example | + +##### Boolean Return Value + +The return type of some methods, e.g. `EXPIRE`, `HSET`, is `bool`. If the method returns `false`, it DOES NOT mean that `Redis` failed to send the command to Redis server. Instead, it means that Redis server returns an *Integer Reply*, and the value of the reply is `0`. Accordingly, if the method returns `true`, it means that Redis server returns an *Integer Reply*, and the value of the reply is `1`. You can +check [Redis commands manual](http://redis.io/commands) for what do `0` and `1` stand for. + +For example, when we send `EXPIRE` command to Redis server, it returns `1` if the timeout was set, and it returns `0` if the key doesn't exist. Accordingly, if the timeout was set, `Redis::expire` returns `true`, and if the key doesn't exist, `Redis::expire` returns `false`. + +So, never use the return value to check if the command has been successfully sent to Redis server. Instead, if `Redis` failed to send command to server, it throws an exception of type `Error`. See the [Exception section](#exception) for details on exceptions. + +##### Optional + +[std::optional](http://en.cppreference.com/w/cpp/utility/optional) is a good option for return type, if Redis might return *NULL REPLY*. However, `std::optional` is introduced in C++ 17 standard, and if you build *redis-plus-plus* with `-std=c++11` standard (i.e. by specifying `-DREDIS_PLUS_PLUS_CXX_STANDARD=11` with cmake command), we implement our own [simple version](https://github.com/sewenew/redis-plus-plus/blob/master/src/sw/redis%2B%2B/cxx11/cxx_utils.h), i.e. `template Optional`. Instead, if you build *redis-plus-plus* with `-std=c++17` standard (i.e. the default behavior), you can use `std::optional`, and we have an alias for it: `template using Optional = std::optional`. + +Take the [GET](https://redis.io/commands/get) and [MGET](https://redis.io/commands/mget) commands for example: + +```C++ +// Or just: auto val = redis.get("key"); +Optional val = redis.get("key"); + +// Optional has a conversion to bool. +// If it's NOT a null Optional object, it's converted to true. +// Otherwise, it's converted to false. +if (val) { + // Key exists. Dereference val to get the string result. + std::cout << *val << std::endl; +} else { + // Redis server returns a NULL Bulk String Reply. + // It's invalid to dereference a null Optional object. + std::cout << "key doesn't exist." << std::endl; +} + +std::vector> values; +redis.mget({"key1", "key2", "key3"}, std::back_inserter(values)); +for (const auto &val : values) { + if (val) { + // Key exist, process the value. + } +} +``` + +We also have some typedefs for some commonly used `Optional`: + +```C++ +using OptionalString = Optional; + +using OptionalLongLong = Optional; + +using OptionalDouble = Optional; + +using OptionalStringPair = Optional>; +``` + +##### Variant + +[std::variant](https://en.cppreference.com/w/cpp/utility/variant) is a good option for return type, if the reply might be of different types. For example, the `MEMORY STATS` command returns an array reply, which is, in fact, a map of key-value pairs of configurations: + +``` +127.0.0.1:6379> memory stats + 1) "peak.allocated" + 2) (integer) 4471104 + ... +17) "db.0" +18) 1) "overhead.hashtable.main" + 2) (integer) 104 + 3) "overhead.hashtable.expires" + 4) (integer) 32 +... +27) "dataset.percentage" +28) "9.70208740234375" +... +``` + +However, as you can see, the value part of the result might be of type long long (key: *peak.allocated*), double (key: *dataset.percentage*) or even a map (key: *db.0*). So you cannot simply parse the result into a `std::unordered_map` or `std::unordered_map`. A workaround is to parse the result into a `tuple`, however, this tuple solution is ugly and error-prone. Check [this issue](https://github.com/sewenew/redis-plus-plus/issues/138) for detail. + +In this case, `Variant`, which is a typedef of `std::variant` if you build redis-plus-plus with C++17 standard, is very helpful. You can parse the result into a `std::unordered_map>>`. + +``` +using Var = Variant>; +auto r = Redis("tcp://127.0.0.1"); +auto v = r.command>("memory", "stats"); +``` + +There're some limitations on `Variant` support: + +- The type arguments of `Variant`, cannot have duplicate items, e.g. `Variant` won't work. +- `double` must be placed before `std::string`. Because `double` reply is, in fact, string reply, and when parsing variant, we try to parse the reply into the first matched type, specified with the type arguments from left to right. So if `double` is placed after `std::string`, i.e. on the right side of `std::string`, the reply will always be parsed into `std::string`. + +Also check the [generic command section](https://github.com/sewenew/redis-plus-plus#generic-command-interface) for more examples on generic command interface. + +##### STL container + +When using generic command interface, instead of parsing the reply to output iterator, you can also parse it into a STL container. + +``` +auto r = Redis("tcp://127.0.0.1"); +auto v = r.command>("config", "get", "*"); +``` + +Also check the [generic command section](https://github.com/sewenew/redis-plus-plus#generic-command-interface) for more examples on generic command interface. + +#### Examples + +Let's see some examples on how to send commands to Redis server. + +##### Various Parameter Types + +```C++ +// ***** Parameters of StringView type ***** + +// Implicitly construct StringView with c-style string. +redis.set("key", "value"); + +// Implicitly construct StringView with std::string. +std::string key("key"); +std::string val("value"); +redis.set(key, val); + +// Explicitly pass StringView as parameter. +std::vector large_data; +// Avoid copying. +redis.set("key", StringView(large_data.data(), large_data.size())); + +// ***** Parameters of long long type ***** + +// For index. +redis.bitcount(key, 1, 3); + +// For number. +redis.incrby("num", 100); + +// ***** Parameters of double type ***** + +// For score. +redis.zadd("zset", "m1", 2.5); +redis.zadd("zset", "m2", 3.5); +redis.zadd("zset", "m3", 5); + +// For (longitude, latitude). +redis.geoadd("geo", std::make_tuple("member", 13.5, 15.6)); + +// ***** Time-related parameters ***** + +using namespace std::chrono; + +redis.expire(key, seconds(1000)); + +auto tp = time_point_cast(system_clock::now() + seconds(100)); +redis.expireat(key, tp); + +// ***** Some options for commands ***** + +if (redis.set(key, "value", milliseconds(100), UpdateType::NOT_EXIST)) { + std::cout << "set OK" << std::endl; +} + +redis.linsert("list", InsertPosition::BEFORE, "pivot", "val"); + +std::vector res; + +// (-inf, inf) +redis.zrangebyscore("zset", UnboundedInterval{}, std::back_inserter(res)); + +// [3, 6] +redis.zrangebyscore("zset", + BoundedInterval(3, 6, BoundType::CLOSED), + std::back_inserter(res)); + +// (3, 6] +redis.zrangebyscore("zset", + BoundedInterval(3, 6, BoundType::LEFT_OPEN), + std::back_inserter(res)); + +// (3, 6) +redis.zrangebyscore("zset", + BoundedInterval(3, 6, BoundType::OPEN), + std::back_inserter(res)); + +// [3, 6) +redis.zrangebyscore("zset", + BoundedInterval(3, 6, BoundType::RIGHT_OPEN), + std::back_inserter(res)); + +// [3, +inf) +redis.zrangebyscore("zset", + LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + std::back_inserter(res)); + +// (3, +inf) +redis.zrangebyscore("zset", + LeftBoundedInterval(3, BoundType::OPEN), + std::back_inserter(res)); + +// (-inf, 6] +redis.zrangebyscore("zset", + RightBoundedInterval(6, BoundType::LEFT_OPEN), + std::back_inserter(res)); + +// (-inf, 6) +redis.zrangebyscore("zset", + RightBoundedInterval(6, BoundType::OPEN), + std::back_inserter(res)); + +// ***** Pair of iterators ***** + +std::vector> kvs = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}}; +redis.mset(kvs.begin(), kvs.end()); + +std::unordered_map kv_map = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}}; +redis.mset(kv_map.begin(), kv_map.end()); + +std::unordered_map str_map = {{"f1", "v1"}, {"f2", "v2"}, {"f3", "v3"}}; +redis.hmset("hash", str_map.begin(), str_map.end()); + +std::unordered_map score_map = {{"m1", 20}, {"m2", 12.5}, {"m3", 3.14}}; +redis.zadd("zset", score_map.begin(), score_map.end()); + +std::vector keys = {"k1", "k2", "k3"}; +redis.del(keys.begin(), keys.end()); + +// ***** Parameters of initializer_list type ***** + +redis.mset({ + std::make_pair("k1", "v1"), + std::make_pair("k2", "v2"), + std::make_pair("k3", "v3") +}); + +redis.hmset("hash", + { + std::make_pair("f1", "v1"), + std::make_pair("f2", "v2"), + std::make_pair("f3", "v3") + }); + +redis.zadd("zset", + { + std::make_pair("m1", 20.0), + std::make_pair("m2", 34.5), + std::make_pair("m3", 23.4) + }); + +redis.del({"k1", "k2", "k3"}); +``` + +##### Various Return Types + +```C++ +// ***** Return void ***** + +redis.save(); + +// ***** Return std::string ***** + +auto info = redis.info(); + +// ***** Return bool ***** + +if (!redis.expire("nonexistent", std::chrono::seconds(100))) { + std::cerr << "key doesn't exist" << std::endl; +} + +if (redis.setnx("key", "val")) { + std::cout << "set OK" << std::endl; +} + +// ***** Return long long ***** + +auto len = redis.strlen("key"); +auto num = redis.del({"a", "b", "c"}); +num = redis.incr("a"); + +// ***** Return double ***** + +auto real = redis.incrbyfloat("b", 23.4); +real = redis.hincrbyfloat("c", "f", 34.5); + +// ***** Return Optional, i.e. OptionalString ***** + +auto os = redis.get("kk"); +if (os) { + std::cout << *os << std::endl; +} else { + std::cerr << "key doesn't exist" << std::endl; +} + +os = redis.spop("set"); +if (os) { + std::cout << *os << std::endl; +} else { + std::cerr << "set is empty" << std::endl; +} + +// ***** Return Optional, i.e. OptionalLongLong ***** + +auto oll = redis.zrank("zset", "mem"); +if (oll) { + std::cout << "rank is " << *oll << std::endl; +} else { + std::cerr << "member doesn't exist" << std::endl; +} + +// ***** Return Optional, i.e. OptionalDouble ***** + +auto ob = redis.zscore("zset", "m1"); +if (ob) { + std::cout << "score is " << *ob << std::endl; +} else { + std::cerr << "member doesn't exist" << std::endl; +} + +// ***** Return Optional> ***** + +auto op = redis.blpop({"list1", "list2"}, std::chrono::seconds(2)); +if (op) { + std::cout << "key is " << op->first << ", value is " << op->second << std::endl; +} else { + std::cerr << "timeout" << std::endl; +} + +// ***** Output iterators ***** + +std::vector os_vec; +redis.mget({"k1", "k2", "k3"}, std::back_inserter(os_vec)); + +std::vector s_vec; +redis.lrange("list", 0, -1, std::back_inserter(s_vec)); + +std::unordered_map hash; +redis.hgetall("hash", std::inserter(hash, hash.end())); +// You can also save the result in a vecotr of string pair. +std::vector> hash_vec; +redis.hgetall("hash", std::back_inserter(hash_vec)); + +std::unordered_set str_set; +redis.smembers("s1", std::inserter(str_set, str_set.end())); +// You can also save the result in a vecotr of string. +s_vec.clear(); +redis.smembers("s1", std::back_inserter(s_vec)); +``` + +##### SCAN Commands + +```C++ +auto cursor = 0LL; +auto pattern = "*pattern*"; +auto count = 5; +std::unordered_set keys; +while (true) { + cursor = redis.scan(cursor, pattern, count, std::inserter(keys, keys.begin())); + // Default pattern is "*", and default count is 10 + // cursor = redis.scan(cursor, std::inserter(keys, keys.begin())); + + if (cursor == 0) { + break; + } +} +``` + +##### Command Overloads + +Sometimes the type of output iterator decides which options to send with the command. + +```C++ +// If the output iterator is an iterator of a container of string, +// we send *ZRANGE* command without the *WITHSCORES* option. +std::vector members; +redis.zrange("list", 0, -1, std::back_inserter(members)); + +// If it's an iterator of a container of a pair, +// we send *ZRANGE* command with *WITHSCORES* option. +std::vector> res_with_score; +redis.zrange("list", 0, -1, std::back_inserter(res_with_score)); + +// The above examples also apply to other command with the *WITHSCORES* options, +// e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, *ZREVRANGEBYSCORE*. + +// Another example is the *GEORADIUS* command. + +// Only get members. +members.clear(); +redis.georadius("geo", + std::make_pair(10.1, 11.1), + 100, + GeoUnit::KM, + 10, + true, + std::back_inserter(members)); + +// If the iterator is an iterator of a container of tuple, +// we send the *GEORADIUS* command with *WITHDIST* option. +std::vector> mem_with_dist; +redis.georadius("geo", + std::make_pair(10.1, 11.1), + 100, + GeoUnit::KM, + 10, + true, + std::back_inserter(mem_with_dist)); + +// If the iterator is an iterator of a container of tuple, +// we send the *GEORADIUS* command with *WITHDIST* and *WITHHASH* options. +std::vector> mem_with_dist_hash; +redis.georadius("geo", + std::make_pair(10.1, 11.1), + 100, + GeoUnit::KM, + 10, + true, + std::back_inserter(mem_with_dist_hash)); + +// If the iterator is an iterator of a container of +// tuple, double>, +// we send the *GEORADIUS* command with *WITHHASH*, *WITHCOORD* and *WITHDIST* options. +std::vector> mem_with_hash_coord_dist; +redis.georadius("geo", + std::make_pair(10.1, 11.1), + 100, + GeoUnit::KM, + 10, + true, + std::back_inserter(mem_with_hash_coord_dist)); +``` + +Please see [redis.h](https://github.com/sewenew/redis-plus-plus/blob/master/src/sw/redis%2B%2B/redis.h) for doxygen style API references and examples, and see the [tests](https://github.com/sewenew/redis-plus-plus/tree/master/test/src/sw/redis%2B%2B) for other examples. + +### Exception + +`Redis` throws exceptions if it receives an *Error Reply* or something bad happens, e.g. failed to create a connection to server, or connection to server is broken. All exceptions derived from `Error` class. See [errors.h](https://github.com/sewenew/redis-plus-plus/blob/master/src/sw/redis%2B%2B/errors.h) for details. + +- `Error`: Generic error. It's derived from `std::exception`, and it's also the base class of other exceptions. +- `IoError`: There's some IO error with the connection. +- `TimeoutError`: Read or write operation was timed out. It's a derived class of `IoError`. +- `ClosedError`: Redis server closed the connection. +- `ProtoError`: The command or reply is invalid, and we cannot process it with Redis protocol. +- `OomError`: *hiredis* library got an out-of-memory error. +- `ReplyError`: Redis server returned an error reply, e.g. we try to call `redis::lrange` on a Redis hash. +- `WatchError`: Watched key has been modified. See [Watch section](#watch) for details. + +**NOTE**: *NULL REPLY* is not taken as an exception. For example, if we try to `GET` a non-existent key, we'll get a *NULL Bulk String Reply*. Instead of throwing an exception, we return the *NULL REPLY* as a null `Optional` object. Also see [Optional section](#optional). + +Normally, when exception happens, you don't need to create a `Redis` object. It's exception safe, and you can reuse the `Redis` object. Even if the connection to Redis server is broken, and it throws some exception, say, `IoError`. The next time when you send command with the `Redis` object, it will try to reconnect to Redis server automatically. This rule also applies to `RedisCluster`. However, if `Pipeline`, `Transcation` and `Subscriber` throws exception, you need to destroy the object, and create a new one. See the corresponding documentation for details. + +#### Examples + +The following is an example on how to catch these exceptions: + +``` +try { + redis.set("key", "value"); + + // Wrong type error + redis.lpush("key", {"a", "b", "c"}); +} catch (const ReplyError &err) { + // WRONGTYPE Operation against a key holding the wrong kind of value + cout << err.what() << endl; +} catch (const TimeoutError &err) { + // reading or writing timeout +} catch (const ClosedError &err) { + // the connection has been closed. +} catch (const IoError &err) { + // there's an IO error on the connection. +} catch (const Error &err) { + // other errors +} +``` + +### Generic Command Interface + +There're too many Redis commands, we haven't implemented all of them. However, you can use the generic `Redis::command` methods to send any commands to Redis. Unlike other client libraries, `Redis::command` doesn't use format string to combine command arguments into a command string. Instead, you can directly pass command arguments of `StringView` type or arithmetic type as parameters of `Redis::command`. For the reason why we don't use format string, please see [this discussion](https://github.com/sewenew/redis-plus-plus/pull/2). + +```C++ +auto redis = Redis("tcp://127.0.0.1"); + +// Redis class doesn't have built-in *CLIENT SETNAME* method. +// However, you can use Redis::command to send the command manually. +redis.command("client", "setname", "name"); +auto val = redis.command("client", "getname"); +if (val) { + std::cout << *val << std::endl; +} + +// NOTE: the following code is for example only. In fact, Redis has built-in +// methods for the following commands. + +// Arguments of the command can be strings. +// NOTE: for SET command, the return value is NOT always void, I'll explain latter. +redis.command("set", "key", "100"); + +// Arguments of the command can be a combination of strings and integers. +auto num = redis.command("incrby", "key", 1); + +// Argument can also be double. +auto real = redis.command("incrbyfloat", "key", 2.3); + +// Even the key of the command can be of arithmetic type. +redis.command("set", 100, "value"); + +val = redis.command("get", 100); + +// If the command returns an array of elements. +std::vector result; +redis.command("mget", "k1", "k2", "k3", std::back_inserter(result)); + +// Or just parse it into a vector. +result = redis.command>("mget", "k1", "k2", "k3"); + +// Arguments of the command can be a range of strings. +auto set_cmd_strs = {"set", "key", "value"}; +redis.command(set_cmd_strs.begin(), set_cmd_strs.end()); + +auto get_cmd_strs = {"get", "key"}; +val = redis.command(get_cmd_strs.begin(), get_cmd_strs.end()); + +// If it returns an array of elements. +result.clear(); +auto mget_cmd_strs = {"mget", "key1", "key2"}; +redis.command(mget_cmd_strs.begin(), mget_cmd_strs.end(), std::back_inserter(result)); +``` + +**NOTE**: The name of some Redis commands is composed with two strings, e.g. *CLIENT SETNAME*. In this case, you need to pass these two strings as two arguments for `Redis::command`. + +```C++ +// This is GOOD. +redis.command("client", "setname", "name"); + +// This is BAD, and will fail to send command to Redis server. +// redis.command("client setname", "name"); +``` + +As I mentioned in the comments, the `SET` command not always returns `void`. Because if you try to set a (key, value) pair with *NX* or *XX* option, you might fail, and Redis will return a *NULL REPLY*. Besides the `SET` command, there're other commands whose return value is NOT a fixed type, you need to parse it by yourself. For example, `Redis::set` method rewrite the reply of `SET` command, and make it return `bool` type, i.e. if no *NX* or *XX* option specified, Redis server will always return an "OK" string, and `Redis::set` returns `true`; if *NX* or *XX* specified, and Redis server returns a *NULL REPLY*, `Redis::set` returns `false`. + +So `Redis` class also has other overloaded `command` methods, these methods return a `ReplyUPtr`, i.e. `std::unique_ptr`, object. Normally you don't need to parse it manually. Instead, you only need to pass the reply to `template T reply::parse(redisReply &)` to get a value of type `T`. Check the [Return Type section](#return-type) for valid `T` types. If the command returns an array of elements, besides calling `reply::parse` to parse the reply to an STL container, you can also call `template reply::to_array(redisReply &reply, Output output)` to parse the result into an array or STL container with an output iterator. + +Let's rewrite the above examples: + +```C++ +auto redis = Redis("tcp://127.0.0.1"); + +redis.command("client", "setname", "name"); +auto r = redis.command("client", "getname"); +assert(r); + +// If the command returns a single element, +// use `reply::parse(redisReply&)` to parse it. +auto val = reply::parse(*r); +if (val) { + std::cout << *val << std::endl; +} + +// Arguments of the command can be strings. +redis.command("set", "key", "100"); + +// Arguments of the command can be a combination of strings and integers. +r = redis.command("incrby", "key", 1); +auto num = reply::parse(*r); + +// Argument can also be double. +r = redis.command("incrbyfloat", "key", 2.3); +auto real = reply::parse(*r); + +// Even the key of the command can be of arithmetic type. +redis.command("set", 100, "value"); + +r = redis.command("get", 100); +val = reply::parse(*r); + +// If the command returns an array of elements. +r = redis.command("mget", "k1", "k2", "k3"); +// Use `reply::to_array(redisReply&, OutputIterator)` to parse the result into an STL container. +std::vector result; +reply::to_array(*r, std::back_inserter(result)); + +// Or just call `reply::parse` to parse it into vector. +result = reply::parse>(*r); + +// Arguments of the command can be a range of strings. +auto get_cmd_strs = {"get", "key"}; +r = redis.command(get_cmd_strs.begin(), get_cmd_strs.end()); +val = reply::parse(*r); + +// If it returns an array of elements. +result.clear(); +auto mget_cmd_strs = {"mget", "key1", "key2"}; +r = redis.command(mget_cmd_strs.begin(), mget_cmd_strs.end()); +reply::to_array(*r, std::back_inserter(result)); +``` + +In fact, there's one more `Redis::command` method: + +```C++ +template +auto command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type; +``` + +However, this method exposes some implementation details, and is only for internal use. You should NOT use this method. + +### Publish/Subscribe + +You can use `Redis::publish` to publish messages to channels. `Redis` randomly picks a connection from the underlying connection pool, and publishes message with that connection. So you might publish two messages with two different connections. + +When you subscribe to a channel with a connection, all messages published to the channel are sent back to that connection. So there's NO `Redis::subscribe` method. Instead, you can call `Redis::subscriber` to create a `Subscriber` and the `Subscriber` maintains a connection to Redis. The underlying connection is a new connection, NOT picked from the connection pool. This new connection has the same `ConnectionOptions` as the `Redis` object. + +With `Subscriber`, you can call `Subscriber::subscribe`, `Subscriber::unsubscribe`, `Subscriber::psubscribe` and `Subscriber::punsubscribe` to send *SUBSCRIBE*, *UNSUBSCRIBE*, *PSUBSCRIBE* and *PUNSUBSCRIBE* commands to Redis. + +#### Thread Safety + +`Subscriber` is NOT thread-safe. If you want to call its member functions in multi-thread environment, you need to synchronize between threads manually. + +#### Exception + +If any of the `Subscriber`'s method throws an exception other than `ReplyError` or `TimeoutError`, you CANNOT use it any more. Instead, you have to destroy the `Subscriber` object, and create a new one. + +#### Subscriber Callbacks + +There are 6 kinds of messages: +- *MESSAGE*: message sent to a channel. +- *PMESSAGE*: message sent to channels of a given pattern. +- *SUBSCRIBE*: message sent when we successfully subscribe to a channel. +- *UNSUBSCRIBE*: message sent when we successfully unsubscribe to a channel. +- *PSUBSCRIBE*: message sent when we successfully subscribe to a channel pattern. +- *PUNSUBSCRIBE*: message sent when we successfully unsubscribe to a channel pattern. + +We call messages of *SUBSCRIBE*, *UNSUBSCRIBE*, *PSUBSCRIBE* and *PUNSUBSCRIBE* types as *META MESSAGE*s. + +In order to process these messages, you can set callback functions on `Subscriber`: +- `Subscriber::on_message(MsgCallback)`: set callback function for messages of *MESSAGE* type, and the callback interface is: `void (std::string channel, std::string msg)`. +- `Subscriber::on_pmessage(PatternMsgCallback)`: set the callback function for messages of *PMESSAGE* type, and the callback interface is: `void (std::string pattern, std::string channel, std::string msg)`. +- `Subscriber::on_meta(MetaCallback)`: set callback function for messages of *META MESSAGE* type, and the callback interface is: `void (Subscriber::MsgType type, OptionalString channel, long long num)`. `type` is an enum, it can be one of the following enum: `Subscriber::MsgType::SUBSCRIBE`, `Subscriber::MsgType::UNSUBSCRIBE`, `Subscriber::MsgType::PSUBSCRIBE`, `Subscriber::MsgType::PUNSUBSCRIBE`, `Subscriber::MsgType::MESSAGE`, and `Subscriber::MsgType::PMESSAGE`. If you haven't subscribe/psubscribe to any channel/pattern, and try to unsubscribe/punsubscribe without any parameter, i.e. unsubscribe/punsubscribe all channels/patterns, *channel* will be null. So the second parameter of meta callback is of type `OptionalString`. + +All these callback interfaces pass `std::string` by value, and you can take their ownership (i.e. `std::move`) safely. + +#### Consume Messages + +You can call `Subscriber::consume` to consume messages published to channels/patterns that the `Subscriber` has been subscribed. + +`Subscriber::consume` waits for message from the underlying connection. If the `ConnectionOptions::socket_timeout` is reached, and there's no message sent to this connection, `Subscriber::consume` throws a `TimeoutError` exception. If `ConnectionOptions::socket_timeout` is `0ms`, `Subscriber::consume` blocks until it receives a message. + +After receiving the message, `Subscriber::consume` calls the callback function to process the message based on message type. However, if you don't set callback for a specific kind of message, `Subscriber::consume` will consume the received message and discard it, i.e. `Subscriber::consume` returns without running the callback. + +#### Examples + +The following example is a common pattern for using `Subscriber`: + +```C++ +// Create a Subscriber. +auto sub = redis.subscriber(); + +// Set callback functions. +sub.on_message([](std::string channel, std::string msg) { + // Process message of MESSAGE type. + }); + +sub.on_pmessage([](std::string pattern, std::string channel, std::string msg) { + // Process message of PMESSAGE type. + }); + +sub.on_meta([](Subscriber::MsgType type, OptionalString channel, long long num) { + // Process message of META type. + }); + +// Subscribe to channels and patterns. +sub.subscribe("channel1"); +sub.subscribe({"channel2", "channel3"}); + +sub.psubscribe("pattern1*"); + +// Consume messages in a loop. +while (true) { + try { + sub.consume(); + } catch (const Error &err) { + // Handle exceptions. + } +} +``` + +If `ConnectionOptions::socket_timeout` is set, you might get `TimeoutError` exception before receiving a message: + +```C++ +while (true) { + try { + sub.consume(); + } catch (const TimeoutError &e) { + // Try again. + continue; + } catch (const Error &err) { + // Handle other exceptions. + } +} +``` + +The above examples use lambda as callback. If you're not familiar with lambda, you can also set a free function as callback. Check [this issue](https://github.com/sewenew/redis-plus-plus/issues/16) for detail. + +### Pipeline + +[Pipeline](https://redis.io/topics/pipelining) is used to reduce *RTT* (Round Trip Time), and speed up Redis queries. *redis-plus-plus* supports pipeline with the `Pipeline` class. + +#### Create Pipeline + +You can create a pipeline with `Redis::pipeline` method, which returns a `Pipeline` object. + +```C++ +ConnectionOptions connection_options; +ConnectionPoolOptions pool_options; + +Redis redis(connection_options, pool_options); + +auto pipe = redis.pipeline(); +``` + +When creating a `Pipeline` object, by default, `Redis::pipeline` method creates a new connection to Redis server. This connection is NOT picked from the connection pool, but a newly created connection. This connection has the same `ConnectionOptions` as other connections in the connection pool. `Pipeline` object maintains the new connection, and all piped commands are sent through this connection. + +**NOTE**: By default, creating a `Pipeline` object is NOT cheap, since it creates a new connection. So you'd better reuse the `Pipeline` object as much as possible. Check [this](#create-pipeline-without-creating-new-connection) to see how to create a `Pipeline` object without creating a new connection. + +#### Send Commands + +You can send Redis commands through the `Pipeline` object. Just like the `Redis` class, `Pipeline` has one or more (overloaded) methods for each Redis command. However, you CANNOT get the replies until you call `Pipeline::exec`. So these methods do NOT return the reply, instead they return the `Pipeline` object itself. And you can chain these methods calls. + +```C++ +pipe.set("key", "val").incr("num").rpush("list", {0, 1, 2}).command("hset", "key", "field", "value"); +``` + +#### Get Replies + +Once you finish sending commands to Redis, you can call `Pipeline::exec` to get replies of these commands. You can also chain `Pipeline::exec` with other commands. + +```C++ +pipe.set("key", "val").incr("num"); +auto replies = pipe.exec(); + +// The same as: +replies = pipe.set("key", "val").incr("num).exec(); +``` + +In fact, these commands won't be sent to Redis, until you call `Pipeline::exec`. So `Pipeline::exec` does 2 work in order: send all piped commands, then get all replies from Redis. + +Also you can call `Pipeline::discard` to discard those piped commands. + +```C++ +pipe.set("key", "val").incr("num"); + +pipe.discard(); +``` + +#### Parse Replies + +`Pipeline::exec` returns a `QueuedReplies` object, which contains replies of all commands that have been sent to Redis. You can use `QueuedReplies::get` method to get and parse the `ith` reply. It has 3 overloads: + +- `template Result get(std::size_t idx)`: Return the `ith` reply as a return value, and you need to specify the return type as tempalte parameter. +- `template void get(std::size_t idx, Output output)`: If the reply is of type *Array Reply*, you can call this method to write the `ith` reply to an output iterator. Normally, compiler will deduce the type of the output iterator, and you don't need to specify the type parameter explicitly. +- `redisReply& get(std::size_t idx)`: If the reply is NOT a fixed type, call this method to get a reference to `redisReply` object. In this case, you need to call `template T reply::parse(redisReply &)` to parse the reply manually. + +Check the [Return Type section](#return-type) for details on the return types of the result. + +```C++ +auto replies = pipe.set("key", "val").incr("num").lrange("list", 0, -1).exec(); + +auto set_cmd_result = replies.get(0); + +auto incr_cmd_result = replies.get(1); + +std::vector list_cmd_result; +replies.get(2, std::back_inserter(list_cmd_result)); +``` + +#### Exception + +If any of `Pipeline`'s method throws an exception other than `ReplyError`, the `Pipeline` object enters an invalid state. You CANNOT use it any more, but only destroy the object, and create a new one. + +#### Thread Safety + +`Pipeline` is NOT thread-safe. If you want to call its member functions in multi-thread environment, you need to synchronize between threads manually. + +#### Create Pipeline Without Creating New Connection + +**YOU MUST CAREFULLY READ ALL WORDS IN THIS SECTION AND THE VERY IMPORTANT NOTES BEFORE USING THIS FEATURE!!!** + +In fact, you can also create a `Pipeline` object with a connection from the underlying connection pool, so that calling `Redis::pipeline` method can be much cheaper (since it doesn't need to create a new connection). + +The prototype of `Redis::pipeline` is as follows: `Pipeline pipeline(bool new_connection = true);`. If `new_connection` is false, the `Pipeline` object will be created with a connection from the underlying pool. + +``` +ConnectionOptions connection_options; +ConnectionPoolOptions pool_options; + +Redis redis(connection_options, pool_options); + +// Create a Pipeline without creating a new connection. +auto pipe = redis.pipeline(false); +``` + +##### VERY IMPORTANT NOTES + +However, in this case, you MUST be very careful, otherwise, you might get bad performance or even dead lock. Because when you run command with `Pipeline` object, it will hold the connection until `Pipeline::exec`, `Pipeline::discard` or `Pipeline`'s destructor is called (the connection will also be released if any method of `Pipeline` throws `Exception`). If the `Pipeline` object holds the connection for a long time, other `Redis` methods might not be able to get a connection from the underlying pool. + +Check the following dead lock example: + +``` +// By defaul, create a `Redis` object with only ONE connection in pool. +// Also by default, the `ConnectionPoolOptions::wait_timeout` is 0ms, +// which means if the pool is empty, `Redis` method will be blocked until +// the pool is not empty. +Redis redis("tcp://127.0.0.1"); + +// Create a `Pipeline` with a connection in the underlying pool. +// In fact, the connection hasn't been fetched from the pool +// until some method of `Pipeline` has been called. +auto pipe = redis.pipeline(false); + +// Now the `Pipeline` object fetches a connection from the pool. +pipe.set("key1", "val"); + +// `Pipeline` object still holds the connection until `Pipeline::exec`, +// `Pipeline::discard` or the destructor is called. +pipe.set("key2", "val"); + +// Try to send a command with `Redis` object. +// However, the pool is empty, since the `Pipeline` object still holds +// the connection, and this call will be blocked forever. +// DEAD LOCK!!! +redis.get("key"); + +// NEVER goes here. +pipe.exec(); +``` + +**BEST PRACTICE**: + +When creating `Pipeline` without creating a new connection: + +- Always set `ConnectionPoolOptions::wait_timeout` larger than 0ms (i.e. when pool is empty, never block forever). +- Avoid doing slow operation between `Pipeline`'s methods. +- Better chain `Pipeline` methods and the `Pipeline::exec` in one statements. +- Better leave `Pipeline` related code in a block scope. + +``` +ConnectionOptions opts; +opts.host = "127.0.0.1"; +opts.port = 6379; +opts.socket_timeout = std::chrono::milliseconds(50); + +ConnectionPoolOptions pool_opts; +pool_opts.size = 3; + +// Always set `wait_timeout` larger than 0ms. +pool_opts.wait_timeout = std::chrono::milliseconds(50); + +auto redis = Redis(opts, pool_opts); + +{ + // Better put `Pipeline` related code in a block scope. + auto pipe = redis.pipeline(false); + + pipe.set("key1", "val"); + + // DON'T run slow operations here, since `Pipeline` object still holds + // the connection, other threads using this `Redis` object, might be blocked. + + pipe.set("key2", "val"); + + // When `Pipeline::exec` finishes, `Pipeline` releases the connection, and returns it to pool. + auto replies = pipe.exec(); + + // This is even better, i.e. chain `Pipeline` methods with `Pipeline::exec`. + replies = pipe.set("key1", "val").set("key2", "val").exec(); +} + +for (auto i = 0; i < 10; ++i) { + // This operation, i.e. creating a `Pipeline` object with connection in pool, is cheap + auto pipe = redis.pipeline(false); + + // Fetch a connection from the underlying pool, and hold it. + pipe.set("key1", "val").set("key2", "val"); + + // Although `Pipeline::exec` and `Pipeline::discard` haven't been called, + // when `Pipeline`'s destructor is called, the connection will also be + // returned to the pool. +} +``` + +### Transaction + +[Transaction](https://redis.io/topics/transactions) is used to make multiple commands runs atomically. + +#### Create Transaction + +You can create a transaction with `Redis::transaction` method, which returns a `Transaction` object. + +```C++ +ConnectionOptions connection_options; +ConnectionPoolOptions pool_options; + +Redis redis(connection_options, pool_options); + +auto tx = redis.transaction(); +``` + +As the `Pipeline` class, `Transaction` maintains a newly created connection to Redis. This connection has the same `ConnectionOptions` as the `Redis` object. + +**NOTE**: Creating a `Transaction` object is NOT cheap, since it creates a new connection. So you'd better reuse the `Transaction` as much as possible. Check [this](#create-transaction-without-creating-new-connection) to see how to create a `Transaction` object without creating a new connection. + +Also you don't need to send [MULTI](https://redis.io/commands/multi) command to Redis. `Transaction` will do that for you automatically. + +#### Send Commands + +`Transaction` shares most of implementation with `Pipeline`. It has the same interfaces as `Pipeline`. You can send commands as what you do with `Pipeline` object. + +```C++ +tx.set("key", "val").incr("num").lpush("list", {0, 1, 2}).command("hset", "key", "field", "val"); +``` + +#### Execute Transaction + +When you call `Transaction::exec`, you explicitly ask Redis to execute those queued commands, and return the replies. Otherwise, these commands won't be executed. Also, you can call `Transaction::discard` to discard the execution, i.e. no command will be executed. Both `Transaction::exec` and `Transaction::discard` can be chained with other commands. + +```C++ +auto replies = tx.set("key", "val").incr("num").exec(); + +tx.set("key", "val").incr("num"); + +// Discard the transaction. +tx.discard(); +``` + +#### Parse Replies + +See [Pipeline's Parse Replies section](#parse-replies) for how to parse the replies. + +#### Piped Transaction + +Normally, we always send multiple commnds in a transaction. In order to improve the performance, you can send these commands in a pipeline. You can create a piped transaction by passing `true` as parameter of `Redis::transaction` method. + +```C++ +// Create a piped transaction +auto tx = redis.transaction(true); +``` + +With this piped transaction, all commands are sent to Redis in a pipeline. + +#### Exception + +If any of `Transaction`'s method throws an exception other than `WatchError` or `ReplyError`, the `Transaction` object enters an invalid state. You CANNOT use it any more, but only destroy the object and create a new one. + +#### Thread Safety + +`Transacation` is NOT thread-safe. If you want to call its member functions in multi-thread environment, you need to synchronize between threads manually. + +#### Watch + +[WATCH is used to provide a check-and-set(CAS) behavior to Redis transactions](https://redis.io/topics/transactions#optimistic-locking-using-check-and-set). + +The `WATCH` command must be sent in the same connection as the transaction. And normally after the `WATCH` command, we also need to send some other commands to get data from Redis before executing the transaction. Take the following check-and-set case as an example: + +``` +WATCH key // watch a key +val = GET key // get value of the key +new_val = val + 1 // incr the value +MULTI // begin the transaction +SET key new_val // set value only if the value is NOT modified by others +EXEC // try to execute the transaction. + // if val has been modified, the transaction won't be executed. +``` + +However, with `Transaction` object, you CANNOT get the result of commands until the whole transaction has been finished. Instead, you need to create a `Redis` object from the `Transaction` object. The created `Redis` object shares the connection with `Transaction` object. With this created `Redis` object, you can send `WATCH` command and any other Redis commands to Redis server, and get the result immediately. + +Let's see how to implement the above example with *redis-plus-plus*: + +```C++ +auto redis = Redis("tcp://127.0.0.1"); + +// Create a transaction. +auto tx = redis.transaction(); + +// Create a Redis object from the Transaction object. Both objects share the same connection. +auto r = tx.redis(); + +// If the watched key has been modified by other clients, the transaction might fail. +// So we need to retry the transaction in a loop. +while (true) { + try { + // Watch a key. + r.watch("key"); + + // Get the old value. + auto val = r.get("key"); + auto num = 0; + if (val) { + num = std::stoi(*val); + } // else use default value, i.e. 0. + + // Incr value. + ++num; + + // Execute the transaction. + auto replies = tx.set("key", std::to_string(num)).exec(); + + // Transaction has been executed successfully. Check the result and break. + + assert(replies.size() == 1 && replies.get(0) == true); + + break; + } catch (const WatchError &err) { + // Key has been modified by other clients, retry. + continue; + } catch (const Error &err) { + // Something bad happens, and the Transaction object is no longer valid. + throw; + } +} +``` + +**NOTE**: in the example above, we create `Transaction` object outside the while loop, in order to avoid creating new connection again and again. + +#### Create Transaction Without Creating New Connection + +**NOTE**: YOU MUST CAREFULLY READ ALL WORDS AND THE VERY IMPORTANT NOTES LINK IN THIS SECTION BEFORE USING THIS FEATURE!!! + +In fact, you can also create a `transaction` object with a connection from the underlying connection pool, so that calling `Redis::transaction` method can be much cheaper (since it doesn't need to create a new connection). + +The prototype of `Redis::transaction` is as follows: `Transaction transaction(bool piped = false, bool new_connection = true);`. If `new_connection` is false, the `Transaction` object will be created with a connection from the underlying pool. + +``` +ConnectionOptions connection_options; +ConnectionPoolOptions pool_options; + +Redis redis(connection_options, pool_options); + +// Create a Transaction without creating a new connection. +auto tx = redis.transaction(false, false); +``` + +However, in this case, you MUST be very careful, otherwise, you might get bad performance or even dead lock. Please carefully check the similar pipeline's [VERY IMPORTANT NOTES section](#very-important-notes), before you use it! + +Besides those very important notes, there's another important note for `Transaction`: + +- Limit the scope of `Redis` object created by `Transaction::Redis`, i.e. destroy it ASAP. + +Check the following example: + +```C++ +auto redis = Redis(opts, pool_opts); + +// Create a `Transaction` object without creating a new connection. +auto tx = redis.Transaction(false, false); + +// Create a `Redis`, and this `Redis` object shares the same connection with the `Transaction` object. +auto r = tx.redis(); + +// Other code here... + +// Execute the transaction. +auto replies = tx.set("key", "val").exec(); + +// Although `Transaction::exec` has been called, the connection has not been returned to pool. +// Because the `Redis` object, i.e. `r`, still holds the connection. +``` + +So the above watch example should be modified as follows: + +```C++ +auto redis = Redis(opts, pool_opts); + +// If the watched key has been modified by other clients, the transaction might fail. +// So we need to retry the transaction in a loop. +while (true) { + try { + // Create a transaction without creating a new connection. + auto tx = redis.transaction(false, false); + + // Create a Redis object from the Transaction object. Both objects share the same connection. + auto r = tx.redis(); + + // Watch a key. + r.watch("key"); + + // Get the old value. + auto val = r.get("key"); + auto num = 0; + if (val) { + num = std::stoi(*val); + } // else use default value, i.e. 0. + + // Incr value. + ++num; + + // Execute the transaction. + auto replies = tx.set("key", std::to_string(num)).exec(); + + // Transaction has been executed successfully. Check the result and break. + + assert(replies.size() == 1 && replies.get(0) == true); + + break; + } catch (const WatchError &err) { + // Key has been modified by other clients, retry. + continue; + } catch (const Error &err) { + // Something bad happens, and the Transaction object is no longer valid. + throw; + } +} +``` + +**NOTE**: The difference is that we create the `Transaction` object in the while loop (it's cheap, since it doesn't need to create a new connection). When the `Transaction` object and the `Redis` object created by `Transaction::redis` have been destroyed, the connection will be return to pool. + +### Redis Cluster + +*redis-plus-plus* supports [Redis Cluster](https://redis.io/topics/cluster-tutorial). You can use `RedisCluster` class to send commands to Redis Cluster. It has similar interfaces as `Redis` class. + +#### Connection + +By default, `RedisCluster` connects to all master nodes in the cluster. For each master node, it maintains a connection pool. If you want to read from slave nodes, you need to explicitly set an option (see [below](#read-from-replica) for reference). + +You can initialize a `RedisCluster` instance with `ConnectionOptions` and `ConnectionPoolOptions`. You only need to set one master node's host & port in `ConnectionOptions`, and `RedisCluster` will get other nodes' info automatically (with the *CLUSTER SLOTS* command). For each master node, it creates a connection pool with the specified `ConnectionPoolOptions`. If `ConnectionPoolOptions` is not specified, `RedisCluster` maintains a single connection to every master node. + +```C++ +// Set a master node's host & port. +ConnectionOptions connection_options; +connection_options.host = "127.0.0.1"; // Required. +connection_options.port = 7000; // Optional. The default port is 6379. +connection_options.password = "auth"; // Optional. No password by default. + +// Automatically get other nodes' info, +// and connect to every master node with a single connection. +RedisCluster cluster1(connection_options); + +ConnectionPoolOptions pool_options; +pool_options.size = 3; + +// For each master node, maintains a connection pool of size 3. +RedisCluster cluster2(connection_options, pool_options); +``` + +You can also specify connection option with an URI. However, in this way, you can only use default `ConnectionPoolOptions`, i.e. pool of size 1, and CANNOT specify password. + +```C++ +// Specify a master node's host & port. +RedisCluster cluster3("tcp://127.0.0.1:7000"); + +// Use default port, i.e. 6379. +RedisCluster cluster4("tcp://127.0.0.1"); +``` + +##### Read From Replica + +If you want to scale read by reading (possible stale) data from slave nodes, you can specifiy `Role::SLAVE` as the third parameter of `RedisCluster`'s constructor. In this case, *redis-plus-plus* will randomly pick a replica node for each master node of the cluster, and create a connection pool for the replica node. + +```C++ +RedisCluster cluster(connection_options, pool_options, Role::SLAVE); + +auto val = cluster.get("key"); +``` + +In this case, you can only send readonly commands to Redis Cluster. If you try to send a write command, e.g. `set`, `hset`, *redis-plus-plus* will throw an exception. Currently, *redis-plus-plus* doesn't handle this case, i.e. sending write command in `Role::SLAVE` mode, elegantly, and you might get some performance problem. So, NEVER send write command in `Role::SLAVE` mode. I'll fix this issue in the future. + +**NOTE**: In `Role::SLAVE` mode, you don't need to manually send [READONLY](https://redis.io/commands/readonly) command to slave nodes. Instead, *redis-plus-plus* will send *READONLY* command to slave nodes automatically. + +##### Note + +- `RedisCluster` only works with tcp connection. It CANNOT connect to Unix Domain Socket. If you specify Unix Domain Socket in `ConnectionOptions`, it throws an exception. +- All nodes in the cluster should have the same password. +- Since [Redis Cluster does NOT support multiple databses](https://redis.io/topics/cluster-spec#implemented-subset), `ConnectionOptions::db` is ignored. + +#### Interfaces + +As we mentioned above, `RedisCluster`'s interfaces are similar to `Redis`. It supports most of `Redis`' interfaces, including the [generic command interface](#generic-command-interface) (see `Redis`' [API Reference section](#api-reference) for details), except the following: + +- Not support commands without key as argument, e.g. `PING`, `INFO`. +- Not support Lua script without key parameters. + +Since there's no key parameter, `RedisCluster` has no idea on to which node these commands should be sent. However there're 2 workarounds for this problem: + +- If you want to send these commands to a specific node, you can create a `Redis` object with that node's host and port, and use the `Redis` object to do the work. +- Instead of host and port, you can also call `Redis RedisCluster::redis(const StringView &hash_tag)` to create a `Redis` object with a hash-tag specifying the node. In this case, the returned `Redis` object creates a new connection to Redis server. **NOTE**: the returned `Redis` object, **IS NOT THREAD SAFE!**. Also, when using the returned `Redis` object, if it throws exception, you need to destroy it, and create a new one with the `RedisCluster::redis` method. + +Also you can use the [hash tags](https://redis.io/topics/cluster-spec#keys-hash-tags) to send multiple-key commands. + +See the [example section](#examples-2) for details. + +##### Publish/Subscribe + +You can publish and subscribe messages with `RedisCluster`. The interfaces are exactly the same as `Redis`, i.e. use `RedisCluster::publish` to publish messages, and use `RedisCluster::subscriber` to create a subscriber to consume messages. See [Publish/Subscribe section](#publishsubscribe) for details. + +##### Pipeline and Transaction + +You can also create `Pipeline` and `Transaction` objects with `RedisCluster`, but the interfaces are different from `Redis`. Since all commands in the pipeline and transaction should be sent to a single node in a single connection, we need to tell `RedisCluster` with which node the pipeline or transaction should be created. + +Instead of specifying the node's IP and port, `RedisCluster`'s pipeline and transaction interfaces allow you to specify the node with a *hash tag*. `RedisCluster` will calculate the slot number with the given *hash tag*, and create a pipeline or transaction with the node holding the slot. + +```C++ +Pipeline RedisCluster::pipeline(const StringView &hash_tag, bool new_connection = true); + +Transaction RedisCluster::transaction(const StringView &hash_tag, bool piped = false, bool new_connection = true); +``` + +With the created `Pipeline` or `Transaction` object, you can send commands with keys located on the same node as the given *hash_tag*. See [Examples section](#examples-2) for an example. + +**NOTE**: By default, `Pipeline` and `Transaction` will be created with a new connection. In order to avoid creating new connection, you can pass `false` as the last parameter. However, in this case, you MUST be very careful, otherwise, you might get bad performance or even dead lock. Please carefully check the related [pipeline section](#very-important-notes) before using this feature. + +#### Examples + +```C++ +#include + +using namespace sw::redis; + +auto redis_cluster = RedisCluster("tcp://127.0.0.1:7000"); + +redis_cluster.set("key", "value"); +auto val = redis_cluster.get("key"); +if (val) { + std::cout << *val << std::endl; +} + +// With hash-tag. +redis_cluster.set("key{tag}1", "val1"); +redis_cluster.set("key{tag}2", "val2"); +redis_cluster.set("key{tag}3", "val3"); +std::vector hash_tag_res; +redis_cluster.mget({"key{tag}1", "key{tag}2", "key{tag}3"}, + std::back_inserter(hash_tag_res)); + +redis_cluster.lpush("list", {"1", "2", "3"}); +std::vector list; +redis_cluster.lrange("list", 0, -1, std::back_inserter(list)); + +// Pipeline. +auto pipe = redis_cluster.pipeline("counter"); +auto replies = pipe.incr("{counter}:1").incr("{counter}:2").exec(); + +// Transaction. +auto tx = redis_cluster.transaction("key"); +replies = tx.incr("key").get("key").exec(); + +// Create a Redis object with hash-tag. +// It connects to the Redis instance that holds the given key, i.e. hash-tag. +auto r = redis_cluster.redis("hash-tag"); + +// And send command without key parameter to the server. +r.command("client", "setname", "connection-name"); +``` + +**NOTE**: By default, when you use `RedisCluster::redis(const StringView &hash_tag, bool new_connection = true)` to create a `Redis` object, instead of picking a connection from the underlying connection pool, it creates a new connection to the corresponding Redis server. So this is NOT a cheap operation, and you should try to reuse this newly created `Redis` object as much as possible. If you pass `false` as the second parameter, you can create a `Redis` object without creating a new connection. However, in this case, you should be very careful, otherwise, you might get bad performance or even dead lock. Please carefully check the related [pipeline section](#very-important-notes) before using this feature. + +```C++ +// This is BAD! It's very inefficient. +// NEVER DO IT!!! +// After sending PING command, the newly created Redis object will be destroied. +cluster.redis("key").ping(); + +// Then it creates a connection to Redis, and closes the connection after sending the command. +cluster.redis("key").command("client", "setname", "hello"); + +// Instead you should reuse the Redis object. +// This is GOOD! +auto redis = cluster.redis("key"); + +redis.ping(); +redis.command("client", "setname", "hello"); + +// This is GOOD! Create `Redis` object without creating a new connection. Use it, and destroy it ASAP. +cluster.redis("key", false).ping(); +``` + +#### Details + +`RedisCluster` maintains the newest slot-node mapping, and sends command directly to the right node. Normally it works as fast as `Redis`. If the cluster reshards, `RedisCluster` will follow the redirection, and it will finally update the slot-node mapping. It can correctly handle the following resharding cases: + +- Data migration between exist nodes. +- Add new node to the cluster. +- Remove node from the cluster. + +`redis-plus-plus` is able to handle both [MOVED](https://redis.io/topics/cluster-spec#moved-redirection) and [ASK](https://redis.io/topics/cluster-spec#ask-redirection) redirections, so it's a complete Redis Cluster client. + +If master is down, the cluster will promote one of its replicas to be the new master. *redis-plus-plus* can also handle this case: + +- When the master is down, *redis-plus-plus* losts connection to it. In this case, if you try to send commands to this master, *redis-plus-plus* will try to update slot-node mapping from other nodes. If the mapping remains unchanged, i.e. new master hasn't been elected yet, it fails to send command to Redis Cluster and throws exception. +- When the new master has been elected, the slot-node mapping will be updated by the cluster. In this case, if you send commands to the cluster, *redis-plus-plus* can get an update-to-date mapping, and sends commands to the new master. + +### Redis Sentinel + +[Redis Sentinel provides high availability for Redis](https://redis.io/topics/sentinel). If Redis master is down, Redis Sentinels will elect a new master from slaves, i.e. failover. Besides, Redis Sentinel can also act like a configuration provider for clients, and clients can query master or slave address from Redis Sentinel. So that if a failover occurs, clients can ask the new master address from Redis Sentinel. + +*redis-plus-plus* supports getting Redis master or slave's IP and port from Redis Sentinel. In order to use this feature, you only need to initialize `Redis` object with Redis Sentinel info, which is composed with 3 parts: `std::shared_ptr`, master name and role (master or slave). + +Before using Redis Sentinel with *redis-plus-plus*, ensure that you have read Redis Sentinel's [doc](https://redis.io/topics/sentinel). + +#### Sentinel + +You can create a `std::shared_ptr` object with `SentinelOptions`. + +```C++ +SentinelOptions sentinel_opts; +sentinel_opts.nodes = {{"127.0.0.1", 9000}, + {"127.0.0.1", 9001}, + {"127.0.0.1", 9002}}; // Required. List of Redis Sentinel nodes. + +// Optional. Timeout before we successfully connect to Redis Sentinel. +// By default, the timeout is 100ms. +sentinel_opts.connect_timeout = std::chrono::milliseconds(200); + +// Optional. Timeout before we successfully send request to or receive response from Redis Sentinel. +// By default, the timeout is 100ms. +sentinel_opts.socket_timeout = std::chrono::milliseconds(200); + +auto sentinel = std::make_shared(sentinel_opts); +``` + +`SentinelOptions::connect_timeout` and `SentinelOptions::socket_timeout` CANNOT be 0ms, i.e. no timeout and block forever. Otherwise, *redis-plus-plus* will throw an exception. + +See [SentinelOptions](https://github.com/sewenew/redis-plus-plus/blob/master/src/sw/redis%2B%2B/sentinel.h#L33) for more options. + +#### Role + +Besides `std::shared_ptr` and master name, you also need to specify a role. There are two roles: `Role::MASTER`, and `Role::SLAVE`. + +With `Role::MASTER`, *redis-plus-plus* will always connect to current master instance, even if a failover occurs. Each time when *redis-plus-plus* needs to create a new connection to master, or a connection is broken, and it needs to reconnect to master, *redis-plus-plus* will ask master address from Redis Sentinel, and connects to current master. If a failover occurs, *redis-plus-plus* can automatically get the address of the new master, and refresh all connections in the underlying connection pool. + +Similarly, with `Role::SLAVE`, *redis-plus-plus* will always connect to a slave instance. A master might have several slaves, *redis-plus-plus* will randomly pick one, and connect to it, i.e. all connections in the underlying connection pool, connect to the same slave instance (check [this discussion](https://github.com/sewenew/redis-plus-plus/issues/99) on why *redis-plus-plus* not connect to all slaves). If the connection is broken, while this slave instance is still an alive slave, *redis-plus-plus* will reconnect to this slave. However, if this slave instance is down, or it has been promoted to be the master, *redis-plus-plus* will randomly connect to another slave. If there's no slave alive, it throws an exception. + +#### Create Redis With Sentinel + +When creating a `Redis` object with sentinel, besides the sentinel info, you should also provide `ConnectionOptions` and `ConnectionPoolOptions`. These two options are used to connect to Redis instance. `ConnectionPoolOptions` is optional, if not specified, it creates a single connection the instance. + +```C++ +ConnectionOptions connection_opts; +connection_opts.password = "auth"; // Optional. No password by default. +connection_opts.connect_timeout = std::chrono::milliseconds(100); // Required. +connection_opts.socket_timeout = std::chrono::milliseconds(100); // Required. + +ConnectionPoolOptions pool_opts; +pool_opts.size = 3; // Optional. The default size is 1. + +auto redis = Redis(sentinel, "master_name", Role::MASTER, connection_opts, pool_opts); +``` + +You might have noticed that we didn't specify the `host` and `port` fields for `ConnectionOptions`. Because, `Redis` will get these info from Redis Sentinel. Also, in this case, `ConnectionOptions::connect_timeout` and `ConnectionOptions::socket_timeout` CANNOT be 0ms, otherwise, it throws an exception. So you always need to specify these two timeouts manually. + +After creating the `Redis` object with sentinel, you can send commands with it, just like an ordinary `Redis` object. + +If you want to write to master, and scale read with slaves. You can use the following pattern: + +```C++ +auto sentinel = std::make_shared(sentinel_opts); + +auto master = Redis(sentinel, "master_name", Role::MASTER, connection_opts, pool_opts); + +auto slave = Redis(sentinel, "master_name", Role::SLAVE, connection_opts, pool_opts); + +// Write to master. +master.set("key", "value"); + +// Read from slave. +slave.get("key"); +``` + +### Redis Stream + +Since Redis 5.0, it introduces a new data type: *Redis Stream*. *redis-plus-plus* has built-in methods for all stream commands except the *XINFO* command (of course, you can use the [Generic Command Interface](#generic-command-interface) to send *XINFO* command). + +However, the replies of some streams commands, i.e. *XPENDING*, *XREAD*, are complex. So I'll give some examples to show you how to work with these built-in methods. + +#### Examples + +```C++ +auto redis = Redis("tcp://127.0.0.1"); + +using Attrs = std::vector>; + +// You can also use std::unordered_map, if you don't care the order of attributes: +// using Attrs = std::unordered_map; + +Attrs attrs = { {"f1", "v1"}, {"f2", "v2"} }; + +// Add an item into the stream. This method returns the auto generated id. +auto id = redis.xadd("key", "*", attrs.begin(), attrs.end()); + +// Each item is assigned with an id: pair>. +// NOTE: the attribute part might be nil reply, check [this issue](https://github.com/sewenew/redis-plus-plus/issues/283) for detail. +using Item = std::pair>; +using ItemStream = std::vector; + +// If you don't care the order of items in the stream, you can also use unordered_map: +// using ItemStream = std::unordered_map; + +// Read items from a stream, and return at most 10 items. +// You need to specify a key and an id (timestamp + offset). +std::unordered_map result; +redis.xread("key", id, 10, std::inserter(result, result.end())); + +// Read from multiple streams. For each stream, you need to specify a key and an id. +std::unordered_map keys = { {"key", id}, {"another-key", "0-0"} }; +redis.xread(keys.begin(), keys.end(), 10, std::inserter(result, result.end())); + +// Block for at most 1 second if currently there's no data in the stream. +redis.xread("key", id, std::chrono::seconds(1), 10, std::inserter(result, result.end())); + +// Block for multiple streams. +redis.xread(keys.begin(), keys.end(), std::chrono::seconds(1), 10, std::inserter(result, result.end())); + +// Read items in a range: +ItemStream item_stream; +redis.xrange("key", "-", "+", std::back_inserter(item_stream)); + +// Trim the stream to a given number of items. After the operation, the stream length is NOT exactly +// 10. Instead, it might be much larger than 10. +// `XTRIM key MAXLEN 10` +redis.xtrim("key", 10); + +// In order to trim the stream to exactly 10 items, specify the third argument, i.e. approx, as false. +// `XTRIM key MAXLEN ~ 10` +redis.xtrim("key", 10, false); + +// Delete an item from the stream. +redis.xdel("key", id); + +// Create a consumer group. +redis.xgroup_create("key", "group", "$"); + +// If the stream doesn't exist, you can set the fourth argument, i.e. MKSTREAM, to be true. +// redis.xgroup_create("key", "group", "$", true); + +id = redis.xadd("key", "*", attrs.begin(), attrs.end()); + +// Read item by a consumer of a consumer group. +redis.xreadgroup("group", "consumer", "key", ">", 1, std::inserter(result, result.end())); + +using PendingItem = std::tuple; +std::vector pending_items; + +// Get pending items of a speicified consumer. +redis.xpending("key", "group", "-", "+", 1, "consumer", std::back_inserter(pending_items)); + +redis.xack("key", "group", id); + +redis.xgroup_delconsumer("key", "group", "consumer"); +redis.xgroup_destroy("key", "group"); +``` + +If you have any problem on sending stream commands to Redis, please feel free to let me know. + +### Redis Modules + +[Redis Modules](https://redis.io/modules) enrich Redis. However, *redis-plus-plus* does not have built-in support/method for these modules, although you can use the [generic interface](#generic-command-interface) to send commands related to these modules. + +Fortunately, [@wingunder](https://github.com/wingunder) did a great job to make the work easier. He wrote [redis-plus-plus-modules](https://github.com/wingunder/redis-plus-plus-modules), which is a header only project that has built-in support for some popular modules. If you need to work with Redis Modules, you should have a try. + +@wingunder also contributes a lot to *redis-plus-plus*. Many thanks to @wingunder! + +### Async Interface + +*redis-plus-plus* also supports async interface, however, async support for Transaction and Subscriber is still on the way. + +The async interface depends on third-party event library, and so far, only libuv is supported. + +#### Installation + +You must install *libuv*(e.g. *apt-get install libuv1-dev*) before install *hiredis* and *redis-plus-plus*. + +*hiredis* v1.0.0's async interface is different from older version, and *redis-plus-plus* only supports *hiredis* v1.0.0 or later. So you need to ensure you've installed the right version of hiredis before installing *redis-plus-plus*. Also, you should NEVER install multiple versions of *hiredis*, otherwise, you'll get some wired problems. If you already installed an older version, remove it, and install a newer version. + +When installing *redis-plus-plus*, you should specify the following command line option: `-DREDIS_PLUS_PLUS_BUILD_ASYNC=libuv`. + +``` +cmake -DCMAKE_PREFIX_PATH=/installation/path/to/libuv/and/hiredis -DREDIS_PLUS_PLUS_BUILD_ASYNC=libuv .. + +make + +make install +``` + +#### Getting Started + +The async interface is similar to sync interface, except that you should include *sw/redis++/async_redis++.h*, and define an object of `sw::redis::AsyncRedis`, and the related methods return `Future` object (so far, only `std::future` and `boost::future` are supported, support for other implementations of *future* is on the way). + +**NOTE**: When building your application code, don't forget to link libuv. + +``` +#include + +ConnectionOptions opts; +opts.host = "127.0.0.1"; +opts.port = 6379; + +ConnectionPoolOptions pool_opts; +pool_opts.size = 3; + +auto async_redis = AsyncRedis(opts, pool_opts); + +Future ping_res = async_redis.ping(); + +Future set_res = async_redis.set("key", "val"); + +Future> get_res = async_redis.get("key"); + +unordered_map m = {{"a", "b"}, {"c", "d"}}; +Future hmset_res = async_redis.hmset("hash", m.begin(), m.end()); + +auto hgetall_res = async_redis.hgetall>("hash"); + +cout << ping_res.get() << endl; +cout << set_res.get() << endl; +auto val = get_res.get(); +if (val) + cout << *val << endl; +else + cout << "not exist" << endl; + +hmset_res.get(); + +for (const auto &ele : hgetall_res.get()) + cout << ele << endl; + +// Generic interface. + +// There's no *AsyncRedis::client_getname* interface. +// But you can use *Redis::command* to get the client name. +auto getname_res = redis.command("client", "getname"); +val = getname_res.get(); +if (val) { + std::cout << *val << std::endl; +} +``` + +#### Redis Sentinel + +Aysnc interface also supports Redis Sentinel. + +``` +#include + +SentinelOptions sentinel_opts; +sentinel_opts.nodes = { + {"127.0.0.1", 8000}, + {"127.0.0.1", 8001}, + {"127.0.0.1", 8002} +}; + +sentinel_opts.connect_timeout = std::chrono::milliseconds(100); +sentinel_opts.socket_timeout = std::chrono::milliseconds(100); + +auto sentinel = std::make_shared(sentinel_opts); + +onnectionOptions connection_opts; +connection_opts.connect_timeout = std::chrono::milliseconds(100); // Required. +connection_opts.socket_timeout = std::chrono::milliseconds(100); // Required. + +ConnectionPoolOptions pool_opts; +pool_opts.size = 3; // Optional. The default size is 1. + +// Connect to master node. +AsyncRedis redis(sentinel, "mymaster", Role::MASTER, connection_opts, pool_opts); + +// The following code randomly connects to one of the slave nodes. +// AsyncRedis redis(sentinel, "mymaster", Role::SLAVE, connection_opts, pool_opts); + +redis.set("key", "value"); + +auto value = redis.get("key").get(); +``` + +The async support for sentinel is similar with the sync one, except that you need to create an `AsyncSentinel` object instead of a `Sentinel` object. Check [Redis Sentinel](#redis-sentinel) for more details on `SentinelOptions`, `ConnectionOptions` and `Role`. + +#### Redis Cluster + +Aysnc interface also supports Redis Cluster. Instead of `AsyncRedis`, you need to create an `AsyncRedisCluster` object. + +``` +ConnectionOptions opts; +opts.host = "127.0.0.1"; +opts.port = 6379; + +ConnectionPoolOptions pool_opts; +pool_opts.size = 3; + +auto async_cluster = AsyncRedisCluster(opts, pool_opts); + +Future set_res = async_cluster.set("key", "val"); + +Future> get_res = async_cluster.get("key"); + +auto mget_res = async_cluster.mget>({"{hashtag}key1", "{hashhag}key2", "{hashtag}key3"}); + +unordered_map m = {{"a", "b"}, {"c", "d"}}; +Future hmset_res = async_redis.hmset("hash", m.begin(), m.end()); +``` + +#### Event Loop + +By default, `AsyncRedis` and `AsyncRedisCluster` create a default event loop, and runs the loop in a dedicated thread to handle read and write operations. However, you can also share the underlying event loop with multiple `AsyncRedis` and `AsyncRedisCluster` objects. In order to do that, you need to create a `std::shared_ptr`, and pass it to the constructors of `AsyncRedis` and `AsyncRedisCluster`. + +``` +auto event_loop = std::make_shared(); + +auto redis = AsyncRedis(connection_opts, pool_opts, loop); + +auto cluster = AsyncRedisCluster(connection_opts, pool_opts, Role::MASTER, loop); +``` + +#### Future with Continuation + +Unfortunately, `std::future` doesn't support [continuation](https://en.cppreference.com/w/cpp/experimental/future/then) so far, which is inconvenient. However, some other libraries, e.g. boost and folly, have continuation support. + +By default, *redis-plus-plus* returns `std::future` for async interface. However, you can also make it return `boost::future` by specifying `-DREDIS_PLUS_PLUS_ASYNC_FUTURE=boost` when running cmake (`folly` and other libraries might be supported in the future). Of course, in this case, you need to install boost first. + +``` +cmake -DREDIS_PLUS_PLUS_BUILD_ASYNC=libuv -DREDIS_PLUS_PLUS_ASYNC_FUTURE=boost .. +``` + +**NOTE**: When building your application code, don't forget to link boost related libs, e.g. -lboost_thread, -lboost_system. + +Then you can take advantage of `boost::future`'s continuation support: + +``` +#include + +ConnectionOptions opts; +opts.host = "127.0.0.1"; +opts.port = 6379; +auto redis = AsyncRedis(opts); +auto fut = redis.get("key").then([](sw::redis::Future> fut) { + auto val = fut.get(); + if (val) cout << *val << endl; + }); +// Do other things + +// Wait for the continuation finishes. +fut.get(); +``` + +You can also use a thread pool to run the continuation: + +``` +#define BOOST_THREAD_PROVIDES_EXECUTORS + +// You might also need to `#define BOOST_THREAD_USES_MOVE` with some version of Boost. +// See [this issue](https://github.com/sewenew/redis-plus-plus/issues/272) for detail. + +#include +#include + +boost::executors::basic_thread_pool pool(3); +auto fut = redis.get("key").then(pool, + [](sw::redis::Future> fut) { + auto val = fut.get(); + if (val) cout << *val << endl; + }); + +// Do other things + +fut.get(); +``` + +## Redis Recipes + +We can create many interesting data structures and algorithms based on Redis, such as [Redlock](https://redis.io/topics/distlock). We call these data structures and algorithms as **Redis Recipes**. *redis-plus-plus* will support some of these recipes. + +**NOTE**: These recipes will be first implemented on the [recipes branch](https://github.com/sewenew/redis-plus-plus/tree/recipes). I'd like to hear your feedback on the API of these recipes, and when these APIs become stable, I'll merge the code into the master branch. So APIs on the *recipes* branch are NOT stable, and might be changed in the future. + +### Redlock + +[Redlock](https://redis.io/topics/distlock) is a distributed lock based on Redis. Thanks to @wingunder's [suggestion](https://github.com/sewenew/redis-plus-plus/issues/24), *redis-plus-plus* supports Redlock now. @wingunder and I made two different implementation of Redlock: one based on Lua script, and the other based on transaction. The Lua script version should be faster, and also it has many other parameters to control the behavior. However, if you are not allowed to, or don't want to run Lua scripts inside Redis, you could try using the transaction version. + +#### Examples + +``` +auto redis1 = Redis("tcp://127.0.0.1:7000"); +auto redis2 = Redis("tcp://127.0.0.1:7001"); +auto redis3 = Redis("tcp://127.0.0.1:7002"); + +// Lua script version: +{ + RedLockMutex mtx({redis1, redis2, redis3}, "resource"); + + // Not locked. + RedLock lock(mtx, std::defer_lock); + + // Try to get the lock, and keep 30 seconds. + // It returns the validity time of the lock, i.e. the lock is only + // valid in *validity_time*, after that the lock might be acquired by others. + // If failed to acquire the lock, throw an exception of Error type. + auto validity_time = lock.try_lock(std::chrono::seconds(30)); + + // Extend the lock before the lock expired. + validity_time = lock.extend_lock(std::chrono::seconds(10)); + + // You can unlock explicitly. + lock.unlock(); +} // If unlock() is not called, the lock will be unlocked automatically when it's destroied. + +// Transaction version: +{ + RedMutex mtx({redis1, redis2, redis3}, "resource"); + + RedLock lock(mtx, std::defer_lock); + auto validity_time = lock.try_lock(std::chrono::seconds(30)); + validity_time = lock.extend_lock(std::chrono::seconds(30)); + + // You can unlock explicitly. + lock.unlock(); +} +``` + +Please refer to the [code](https://github.com/sewenew/redis-plus-plus/blob/recipes/src/sw/redis%2B%2B/recipes/redlock.h) for detail. I'll enhance the doc in the future. + +## Author + +*redis-plus-plus* is written by sewenew, who is also active on [StackOverflow](https://stackoverflow.com/users/5384363/for-stack). diff --git a/ext/redis-plus-plus-1.3.3/cmake/redis++-config.cmake.in b/ext/redis-plus-plus-1.3.3/cmake/redis++-config.cmake.in new file mode 100644 index 000000000..242f4734d --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/cmake/redis++-config.cmake.in @@ -0,0 +1,12 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +string(REPLACE "," ";" REDIS_PLUS_PLUS_DEPENDS_LIST @REDIS_PLUS_PLUS_DEPENDS@) +foreach(REDIS_PLUS_PLUS_DEP ${REDIS_PLUS_PLUS_DEPENDS_LIST}) + find_dependency(${REDIS_PLUS_PLUS_DEP} REQUIRED) +endforeach() + +include("${CMAKE_CURRENT_LIST_DIR}/redis++-targets.cmake") + +check_required_components(redis++) diff --git a/ext/redis-plus-plus-1.3.3/cmake/redis++.pc.in b/ext/redis-plus-plus-1.3.3/cmake/redis++.pc.in new file mode 100644 index 000000000..42a27c378 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/cmake/redis++.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: redis++ +Description: This is a Redis client, based on hiredis and written in C++11. It supports scritpting, pub/sub, pipeline, transaction, Redis Cluster, Redis Sentinel, connection pool, ACL, SSL and thread safety. +Version: @PROJECT_VERSION@ +URL: https://github.com/sewenew/redis-plus-plus +Requires: @REDIS_PLUS_PLUS_DEPENDS@ +Cflags: -I${includedir} +Libs: -L${libdir} -lredis++ diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cmd_formatter.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cmd_formatter.h new file mode 100644 index 000000000..85efdf347 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cmd_formatter.h @@ -0,0 +1,775 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CMD_FORMATTER_H +#define SEWENEW_REDISPLUSPLUS_CMD_FORMATTER_H + +#include +#include "command_options.h" +#include "command_args.h" +#include "command.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class FormattedCommand { +public: + FormattedCommand(char *data, int len) : _data(data), _size(len) { + if (data == nullptr || len < 0) { + throw Error("failed to format command"); + } + } + + FormattedCommand(const FormattedCommand &) = delete; + FormattedCommand& operator=(const FormattedCommand &) = delete; + + FormattedCommand(FormattedCommand &&that) noexcept { + _move(std::move(that)); + } + + FormattedCommand& operator=(FormattedCommand &&that) noexcept { + if (this != &that) { + _move(std::move(that)); + } + + return *this; + } + + ~FormattedCommand() noexcept { + if (_data != nullptr) { + redisFreeCommand(_data); + } + } + + const char* data() const noexcept { + return _data; + } + + int size() const noexcept { + return _size; + } + +private: + void _move(FormattedCommand &&that) noexcept { + _data = that._data; + _size = that._size; + that._data = nullptr; + that._size = 0; + } + + char *_data = nullptr; + int _size = 0; +}; + +namespace fmt { + +template +FormattedCommand format_cmd(const char *format, Args &&...args) { + char *data = nullptr; + auto len = redisFormatCommand(&data, format, std::forward(args)...); + + return FormattedCommand(data, len); +} + +inline FormattedCommand format_cmd(int argc, const char **argv, const std::size_t *argv_len) { + char *data = nullptr; + auto len = redisFormatCommandArgv(&data, argc, argv, argv_len); + + return FormattedCommand(data, len); +} + +inline FormattedCommand format_cmd(CmdArgs &args) { + char *data = nullptr; + auto len = redisFormatCommandArgv(&data, args.size(), args.argv(), args.argv_len()); + + return FormattedCommand(data, len); +} + +struct SetResultParser { + bool operator()(redisReply &reply) const { + sw::redis::reply::rewrite_set_reply(reply); + return sw::redis::reply::parse(reply); + } +}; + +// CONNECTION commands. + +inline FormattedCommand echo(const StringView &msg) { + return format_cmd("ECHO %b", msg.data(), msg.size()); +} + +inline FormattedCommand ping() { + return format_cmd("PING"); +} + +inline FormattedCommand ping(const StringView &msg) { + return format_cmd("PING %b", msg.data(), msg.size()); +} + +inline FormattedCommand del(const StringView &key) { + return format_cmd("DEL %b", key.data(), key.size()); +} + +template +FormattedCommand del_range(Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "DEL" << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand exists(const StringView &key) { + return format_cmd("EXISTS %b", key.data(), key.size()); +} + +template +FormattedCommand exists_range(Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "EXISTS" << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand expire(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("EXPIRE %b %lld", key.data(), key.size(), timeout.count()); +} + +inline FormattedCommand expireat(const StringView &key, + const std::chrono::time_point &tp) { + return format_cmd("EXPIREAT %b %lld", key.data(), key.size(), tp.time_since_epoch().count()); +} + +inline FormattedCommand pexpire(const StringView &key, + const std::chrono::milliseconds &timeout) { + return format_cmd("PEXPIRE %b %lld", key.data(), key.size(), timeout.count()); +} + +inline FormattedCommand pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return format_cmd("PEXPIREAT %b %lld", key.data(), key.size(), tp.time_since_epoch().count()); +} + +inline FormattedCommand pttl(const StringView &key) { + return format_cmd("PTTL %b", key.data(), key.size()); +} + +inline FormattedCommand rename(const StringView &key, const StringView &newkey) { + return format_cmd("RENAME %b %b", key.data(), key.size(), newkey.data(), newkey.size()); +} + +inline FormattedCommand renamenx(const StringView &key, const StringView &newkey) { + return format_cmd("RENAMENX %b %b", key.data(), key.size(), newkey.data(), newkey.size()); +} + +inline FormattedCommand ttl(const StringView &key) { + return format_cmd("TTL %b", key.data(), key.size()); +} + +inline FormattedCommand unlink(const StringView &key) { + return format_cmd("UNLINK %b", key.data(), key.size()); +} + +template +FormattedCommand unlink_range(Input first, Input last) { + CmdArgs args; + args << "UNLINK" << std::make_pair(first, last); + + return format_cmd(args); +} + +// STRING commands. + +inline FormattedCommand get(const StringView &key) { + return format_cmd("GET %b", key.data(), key.size()); +} + +inline FormattedCommand incr(const StringView &key) { + return format_cmd("INCR %b", key.data(), key.size()); +} + +inline FormattedCommand incrby(const StringView &key, long long increment) { + return format_cmd("INCRBY %b %lld", key.data(), key.size(), increment); +} + +inline FormattedCommand incrbyfloat(const StringView &key, double increment) { + return format_cmd("INCRBYFLOAT %b %f", key.data(), key.size(), increment); +} + +template +FormattedCommand mget(Input first, Input last) { + CmdArgs args; + args << "MGET" << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand mset(Input first, Input last) { + CmdArgs args; + args << "MSET" << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand msetnx(Input first, Input last) { + CmdArgs args; + args << "MSETNX" << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + UpdateType type) { + CmdArgs args; + args << "SET" << key << val; + + if (ttl > std::chrono::milliseconds(0)) { + args << "PX" << ttl.count(); + } + + cmd::detail::set_update_type(args, type); + + return format_cmd(args); +} + +inline FormattedCommand strlen(const StringView &key) { + return format_cmd("STRLEN %b", key.data(), key.size()); +} + +inline FormattedCommand blpop(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BLPOP %b %lld", key.data(), key.size(), timeout.count()); +} + +template +FormattedCommand blpop_range(Input first, Input last, const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BLPOP" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand brpop(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BRPOP %b %lld", key.data(), key.size(), timeout.count()); +} + + +template +FormattedCommand brpop_range(Input first, Input last, const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BRPOP" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return format_cmd("BRPOPLPUSH %b %b %lld", + source.data(), source.size(), + destination.data(), destination.size(), + timeout.count()); +} + +inline FormattedCommand llen(const StringView &key) { + return format_cmd("LLEN %b", key.data(), key.size()); +} + +inline FormattedCommand lpop(const StringView &key) { + return format_cmd("LPOP %b", key.data(), key.size()); +} + +inline FormattedCommand lpush(const StringView &key, const StringView &val) { + return format_cmd("LPUSH %b %b", key.data(), key.size(), val.data(), val.size()); +} + +template +FormattedCommand lpush_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "LPUSH" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand lrange(const StringView &key, long long start, long long stop) { + return format_cmd("LRANGE %b %lld %lld", key.data(), key.size(), start, stop); +} + +inline FormattedCommand lrem(const StringView &key, long long count, const StringView &val) { + return format_cmd("LREM %b %lld %b", key.data(), key.size(), count, val.data(), val.size()); +} + +inline FormattedCommand ltrim(const StringView &key, long long start, long long stop) { + return format_cmd("LTRIM %b %lld %lld", key.data(), key.size(), start, stop); +} + +inline FormattedCommand rpop(const StringView &key) { + return format_cmd("RPOP %b", key.data(), key.size()); +} + +inline FormattedCommand rpoplpush(const StringView &source, const StringView &destination) { + return format_cmd("RPOPLPUSH %b %b", + source.data(), source.size(), + destination.data(), destination.size()); +} + +inline FormattedCommand rpush(const StringView &key, const StringView &val) { + return format_cmd("RPUSH %b %b", key.data(), key.size(), val.data(), val.size()); +} + +template +FormattedCommand rpush_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "RPUSH" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +// HASH commands. + +inline FormattedCommand hdel(const StringView &key, const StringView &field) { + return format_cmd("HDEL %b %b", key.data(), key.size(), field.data(), field.size()); +} + +template +FormattedCommand hdel_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HDEL" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand hexists(const StringView &key, const StringView &field) { + return format_cmd("HEXISTS %b %b", key.data(), key.size(), field.data(), field.size()); +} + +inline FormattedCommand hget(const StringView &key, const StringView &field) { + return format_cmd("HGET %b %b", key.data(), key.size(), field.data(), field.size()); +} + +inline FormattedCommand hgetall(const StringView &key) { + return format_cmd("HGETALL %b", key.data(), key.size()); +} + +inline FormattedCommand hincrby(const StringView &key, + const StringView &field, + long long increment) { + return format_cmd("HINCRBY %b %b %lld", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline FormattedCommand hincrbyfloat(const StringView &key, + const StringView &field, + double increment) { + return format_cmd("HINCRBYFLOAT %b %b %f", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline FormattedCommand hkeys(const StringView &key) { + return format_cmd("HKEYS %b", key.data(), key.size()); +} + +inline FormattedCommand hlen(const StringView &key) { + return format_cmd("HLEN %b", key.data(), key.size()); +} + +template +FormattedCommand hmget(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HMGET" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand hmset(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HMSET" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand hset(const StringView &key, + const StringView &field, + const StringView &val) { + return format_cmd("HSET %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +template +auto hset_range(const StringView &key, + Input first, + Input last) + -> typename std::enable_if::value, + FormattedCommand>::type { + assert(first != last); + + CmdArgs args; + args << "HSET" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand hvals(const StringView &key) { + return format_cmd("HVALS %b", key.data(), key.size()); +} + +// SET commands. + +inline FormattedCommand sadd(const StringView &key, const StringView &member) { + return format_cmd("SADD %b %b", key.data(), key.size(), member.data(), member.size()); +} + +template +FormattedCommand sadd_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SADD" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand scard(const StringView &key) { + return format_cmd("SCARD %b", key.data(), key.size()); +} + +inline FormattedCommand sismember(const StringView &key, const StringView &member) { + return format_cmd("SISMEMBER %b %b", key.data(), key.size(), member.data(), member.size()); +} + +inline FormattedCommand smembers(const StringView &key) { + return format_cmd("SMEMBERS %b", key.data(), key.size()); +} + +inline FormattedCommand spop(const StringView &key) { + return format_cmd("SPOP %b", key.data(), key.size()); +} + +inline FormattedCommand spop(const StringView &key, long long count) { + return format_cmd("SPOP %b %lld", key.data(), key.size(), count); +} + +inline FormattedCommand srem(const StringView &key, const StringView &member) { + return format_cmd("SREM %b %b", key.data(), key.size(), member.data(), member.size()); +} + +template +FormattedCommand srem_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SREM" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +// SORTED SET commands. + +inline FormattedCommand bzpopmax(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BZPOPMAX %b %lld", key.data(), key.size(), timeout.count()); +} + +template +FormattedCommand bzpopmax_range(Input first, + Input last, + const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMAX" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand bzpopmin(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BZPOPMIN %b %lld", key.data(), key.size(), timeout.count()); +} + +template +FormattedCommand bzpopmin_range(Input first, Input last, const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMIN" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type, + bool changed) { + CmdArgs args; + args << "ZADD" << key; + + cmd::detail::set_update_type(args, type); + + if (changed) { + args << "CH"; + } + + args << score << member; + + return format_cmd(args); +} + +template +FormattedCommand zadd_range(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + CmdArgs args; + args << "ZADD" << key; + + cmd::detail::set_update_type(args, type); + + if (changed) { + args << "CH"; + } + + while (first != last) { + // Swap the pair to pair. + args << first->second << first->first; + ++first; + } + + return format_cmd(args); +} + +inline FormattedCommand zcard(const StringView &key) { + return format_cmd("ZCARD %b", key.data(), key.size()); +} + +template +FormattedCommand zcount(const StringView &key, const Interval &interval) { + return format_cmd("ZCOUNT %b %s %s", + key.data(), key.size(), + interval.min().c_str(), + interval.max().c_str()); +} + +inline FormattedCommand zincrby(const StringView &key, + double increment, + const StringView &member) { + return format_cmd("ZINCRBY %b %f %b", + key.data(), key.size(), + increment, + member.data(), member.size()); +} + +template +FormattedCommand zlexcount(const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZLEXCOUNT %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline FormattedCommand zpopmax(const StringView &key) { + return format_cmd("ZPOPMAX %b", key.data(), key.size()); +} + +inline FormattedCommand zpopmax(const StringView &key, long long count) { + return format_cmd("ZPOPMAX %b %lld", key.data(), key.size(), count); +} + +inline FormattedCommand zpopmin(const StringView &key) { + return format_cmd("ZPOPMIN %b", key.data(), key.size()); +} + +inline FormattedCommand zpopmin_count(const StringView &key, long long count) { + return format_cmd("ZPOPMIN %b %lld", key.data(), key.size(), count); +} + +inline FormattedCommand zrange(const StringView &key, long long start, long long stop) { + return format_cmd("ZRANGE %b %lld %lld", key.data(), key.size(), start, stop); +} + +template +FormattedCommand zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); +} + +template +FormattedCommand zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); +} + +inline FormattedCommand zrank(const StringView &key, const StringView &member) { + return format_cmd("ZRANK %b %b", key.data(), key.size(), member.data(), member.size()); +} + +inline FormattedCommand zrem(const StringView &key, const StringView &member) { + return format_cmd("ZREM %b %b", key.data(), key.size(), member.data(), member.size()); +} + +template +FormattedCommand zrem_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "ZREM" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand zremrangebylex(const StringView &key, const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZREMRANGEBYLEX %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline FormattedCommand zremrangebyrank(const StringView &key, long long start, long long stop) { + return format_cmd("ZREMRANGEBYRANK %b %lld %lld", key.data(), key.size(), start, stop); +} + +template +FormattedCommand zremrangebyscore(const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZREMRANGEBYSCORE %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +template +FormattedCommand zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZREVRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); +} + +inline FormattedCommand zrevrank(const StringView &key, const StringView &member) { + return format_cmd("ZREVRANK %b %b", key.data(), key.size(), member.data(), member.size()); +} + +inline FormattedCommand zscore(const StringView &key, const StringView &member) { + return format_cmd("ZSCORE %b %b", key.data(), key.size(), member.data(), member.size()); +} + +// SCRIPTING commands. +template +FormattedCommand eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs args; + auto keys_num = std::distance(keys_first, keys_last); + + args << "EVAL" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + return format_cmd(args); +} + +template +FormattedCommand evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs args; + auto keys_num = std::distance(keys_first, keys_last); + + args << "EVALSHA" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + return format_cmd(args); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CMD_FORMATTER_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command.h new file mode 100644 index 000000000..81857dd9b --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command.h @@ -0,0 +1,2271 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_H + +#include +#include +#include +#include +#include "connection.h" +#include "command_options.h" +#include "command_args.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace cmd { + +// CONNECTION command. +inline void auth(Connection &connection, const StringView &password) { + connection.send("AUTH %b", password.data(), password.size()); +} + +inline void auth(Connection &connection, const StringView &user, const StringView &password) { + connection.send("AUTH %b %b", + user.data(), user.size(), + password.data(), password.size()); +} + +inline void echo(Connection &connection, const StringView &msg) { + connection.send("ECHO %b", msg.data(), msg.size()); +} + +inline void ping(Connection &connection) { + connection.send("PING"); +} + +inline void quit(Connection &connection) { + connection.send("QUIT"); +} + +inline void ping(Connection &connection, const StringView &msg) { + // If *msg* is empty, Redis returns am empty reply of REDIS_REPLY_STRING type. + connection.send("PING %b", msg.data(), msg.size()); +} + +inline void select(Connection &connection, long long idx) { + connection.send("SELECT %lld", idx); +} + +inline void swapdb(Connection &connection, long long idx1, long long idx2) { + connection.send("SWAPDB %lld %lld", idx1, idx2); +} + +// SERVER commands. + +inline void bgrewriteaof(Connection &connection) { + connection.send("BGREWRITEAOF"); +} + +inline void bgsave(Connection &connection) { + connection.send("BGSAVE"); +} + +inline void dbsize(Connection &connection) { + connection.send("DBSIZE"); +} + +inline void flushall(Connection &connection, bool async) { + if (async) { + connection.send("FLUSHALL ASYNC"); + } else { + connection.send("FLUSHALL"); + } +} + +inline void flushdb(Connection &connection, bool async) { + if (async) { + connection.send("FLUSHDB ASYNC"); + } else { + connection.send("FLUSHDB"); + } +} + +inline void info(Connection &connection) { + connection.send("INFO"); +} + +inline void info(Connection &connection, const StringView §ion) { + connection.send("INFO %b", section.data(), section.size()); +} + +inline void lastsave(Connection &connection) { + connection.send("LASTSAVE"); +} + +inline void save(Connection &connection) { + connection.send("SAVE"); +} + +// KEY commands. + +inline void del(Connection &connection, const StringView &key) { + connection.send("DEL %b", key.data(), key.size()); +} + +template +inline void del_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "DEL" << std::make_pair(first, last); + + connection.send(args); +} + +inline void dump(Connection &connection, const StringView &key) { + connection.send("DUMP %b", key.data(), key.size()); +} + +inline void exists(Connection &connection, const StringView &key) { + connection.send("EXISTS %b", key.data(), key.size()); +} + +template +inline void exists_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "EXISTS" << std::make_pair(first, last); + + connection.send(args); +} + +inline void expire(Connection &connection, + const StringView &key, + long long timeout) { + connection.send("EXPIRE %b %lld", + key.data(), key.size(), + timeout); +} + +inline void expireat(Connection &connection, + const StringView &key, + long long timestamp) { + connection.send("EXPIREAT %b %lld", + key.data(), key.size(), + timestamp); +} + +inline void keys(Connection &connection, const StringView &pattern) { + connection.send("KEYS %b", pattern.data(), pattern.size()); +} + +inline void move(Connection &connection, const StringView &key, long long db) { + connection.send("MOVE %b %lld", + key.data(), key.size(), + db); +} + +inline void persist(Connection &connection, const StringView &key) { + connection.send("PERSIST %b", key.data(), key.size()); +} + +inline void pexpire(Connection &connection, + const StringView &key, + long long timeout) { + connection.send("PEXPIRE %b %lld", + key.data(), key.size(), + timeout); +} + +inline void pexpireat(Connection &connection, + const StringView &key, + long long timestamp) { + connection.send("PEXPIREAT %b %lld", + key.data(), key.size(), + timestamp); +} + +inline void pttl(Connection &connection, const StringView &key) { + connection.send("PTTL %b", key.data(), key.size()); +} + +inline void randomkey(Connection &connection) { + connection.send("RANDOMKEY"); +} + +inline void rename(Connection &connection, + const StringView &key, + const StringView &newkey) { + connection.send("RENAME %b %b", + key.data(), key.size(), + newkey.data(), newkey.size()); +} + +inline void renamenx(Connection &connection, + const StringView &key, + const StringView &newkey) { + connection.send("RENAMENX %b %b", + key.data(), key.size(), + newkey.data(), newkey.size()); +} + +void restore(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + bool replace); + +inline void scan(Connection &connection, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("SCAN %lld MATCH %b COUNT %lld", + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void touch(Connection &connection, const StringView &key) { + connection.send("TOUCH %b", key.data(), key.size()); +} + +template +inline void touch_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "TOUCH" << std::make_pair(first, last); + + connection.send(args); +} + +inline void ttl(Connection &connection, const StringView &key) { + connection.send("TTL %b", key.data(), key.size()); +} + +inline void type(Connection &connection, const StringView &key) { + connection.send("TYPE %b", key.data(), key.size()); +} + +inline void unlink(Connection &connection, const StringView &key) { + connection.send("UNLINK %b", key.data(), key.size()); +} + +template +inline void unlink_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "UNLINK" << std::make_pair(first, last); + + connection.send(args); +} + +inline void wait(Connection &connection, long long numslave, long long timeout) { + connection.send("WAIT %lld %lld", numslave, timeout); +} + +// STRING commands. + +inline void append(Connection &connection, const StringView &key, const StringView &str) { + connection.send("APPEND %b %b", + key.data(), key.size(), + str.data(), str.size()); +} + +inline void bitcount(Connection &connection, + const StringView &key, + long long start, + long long end) { + connection.send("BITCOUNT %b %lld %lld", + key.data(), key.size(), + start, end); +} + +void bitop(Connection &connection, + BitOp op, + const StringView &destination, + const StringView &key); + +template +void bitop_range(Connection &connection, + BitOp op, + const StringView &destination, + Input first, + Input last); + +inline void bitpos(Connection &connection, + const StringView &key, + long long bit, + long long start, + long long end) { + connection.send("BITPOS %b %lld %lld %lld", + key.data(), key.size(), + bit, + start, + end); +} + +inline void decr(Connection &connection, const StringView &key) { + connection.send("DECR %b", key.data(), key.size()); +} + +inline void decrby(Connection &connection, const StringView &key, long long decrement) { + connection.send("DECRBY %b %lld", + key.data(), key.size(), + decrement); +} + +inline void get(Connection &connection, const StringView &key) { + connection.send("GET %b", + key.data(), key.size()); +} + +inline void getbit(Connection &connection, const StringView &key, long long offset) { + connection.send("GETBIT %b %lld", + key.data(), key.size(), + offset); +} + +inline void getrange(Connection &connection, + const StringView &key, + long long start, + long long end) { + connection.send("GETRANGE %b %lld %lld", + key.data(), key.size(), + start, + end); +} + +inline void getset(Connection &connection, + const StringView &key, + const StringView &val) { + connection.send("GETSET %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void incr(Connection &connection, const StringView &key) { + connection.send("INCR %b", key.data(), key.size()); +} + +inline void incrby(Connection &connection, const StringView &key, long long increment) { + connection.send("INCRBY %b %lld", + key.data(), key.size(), + increment); +} + +inline void incrbyfloat(Connection &connection, const StringView &key, double increment) { + connection.send("INCRBYFLOAT %b %f", + key.data(), key.size(), + increment); +} + +template +inline void mget(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MGET" << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void mset(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MSET" << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void msetnx(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MSETNX" << std::make_pair(first, last); + + connection.send(args); +} + +inline void psetex(Connection &connection, + const StringView &key, + long long ttl, + const StringView &val) { + connection.send("PSETEX %b %lld %b", + key.data(), key.size(), + ttl, + val.data(), val.size()); +} + +void set(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + UpdateType type); + +inline void setex(Connection &connection, + const StringView &key, + long long ttl, + const StringView &val) { + connection.send("SETEX %b %lld %b", + key.data(), key.size(), + ttl, + val.data(), val.size()); +} + +inline void setnx(Connection &connection, + const StringView &key, + const StringView &val) { + connection.send("SETNX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void setrange(Connection &connection, + const StringView &key, + long long offset, + const StringView &val) { + connection.send("SETRANGE %b %lld %b", + key.data(), key.size(), + offset, + val.data(), val.size()); +} + +inline void strlen(Connection &connection, const StringView &key) { + connection.send("STRLEN %b", key.data(), key.size()); +} + +// LIST commands. + +inline void blpop(Connection &connection, const StringView &key, long long timeout) { + connection.send("BLPOP %b %lld", + key.data(), key.size(), + timeout); +} + +template +inline void blpop_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BLPOP" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void brpop(Connection &connection, const StringView &key, long long timeout) { + connection.send("BRPOP %b %lld", + key.data(), key.size(), + timeout); +} + +template +inline void brpop_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BRPOP" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void brpoplpush(Connection &connection, + const StringView &source, + const StringView &destination, + long long timeout) { + connection.send("BRPOPLPUSH %b %b %lld", + source.data(), source.size(), + destination.data(), destination.size(), + timeout); +} + +inline void lindex(Connection &connection, const StringView &key, long long index) { + connection.send("LINDEX %b %lld", + key.data(), key.size(), + index); +} + +void linsert(Connection &connection, + const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + +inline void llen(Connection &connection, + const StringView &key) { + connection.send("LLEN %b", key.data(), key.size()); +} + +inline void lpop(Connection &connection, const StringView &key) { + connection.send("LPOP %b", + key.data(), key.size()); +} + +inline void lpush(Connection &connection, const StringView &key, const StringView &val) { + connection.send("LPUSH %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +template +inline void lpush_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "LPUSH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void lpushx(Connection &connection, const StringView &key, const StringView &val) { + connection.send("LPUSHX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void lrange(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("LRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +inline void lrem(Connection &connection, + const StringView &key, + long long count, + const StringView &val) { + connection.send("LREM %b %lld %b", + key.data(), key.size(), + count, + val.data(), val.size()); +} + +inline void lset(Connection &connection, + const StringView &key, + long long index, + const StringView &val) { + connection.send("LSET %b %lld %b", + key.data(), key.size(), + index, + val.data(), val.size()); +} + +inline void ltrim(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("LTRIM %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +inline void rpop(Connection &connection, const StringView &key) { + connection.send("RPOP %b", key.data(), key.size()); +} + +inline void rpoplpush(Connection &connection, + const StringView &source, + const StringView &destination) { + connection.send("RPOPLPUSH %b %b", + source.data(), source.size(), + destination.data(), destination.size()); +} + +inline void rpush(Connection &connection, const StringView &key, const StringView &val) { + connection.send("RPUSH %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +template +inline void rpush_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "RPUSH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void rpushx(Connection &connection, const StringView &key, const StringView &val) { + connection.send("RPUSHX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +// HASH commands. + +inline void hdel(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HDEL %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +template +inline void hdel_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HDEL" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hexists(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HEXISTS %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hget(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HGET %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hgetall(Connection &connection, const StringView &key) { + connection.send("HGETALL %b", key.data(), key.size()); +} + +inline void hincrby(Connection &connection, + const StringView &key, + const StringView &field, + long long increment) { + connection.send("HINCRBY %b %b %lld", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline void hincrbyfloat(Connection &connection, + const StringView &key, + const StringView &field, + double increment) { + connection.send("HINCRBYFLOAT %b %b %f", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline void hkeys(Connection &connection, const StringView &key) { + connection.send("HKEYS %b", key.data(), key.size()); +} + +inline void hlen(Connection &connection, const StringView &key) { + connection.send("HLEN %b", key.data(), key.size()); +} + +template +inline void hmget(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HMGET" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void hmset(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HMSET" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("HSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void hset(Connection &connection, + const StringView &key, + const StringView &field, + const StringView &val) { + connection.send("HSET %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +template +void hset_range(Connection &connection, const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HSET" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hsetnx(Connection &connection, + const StringView &key, + const StringView &field, + const StringView &val) { + connection.send("HSETNX %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +inline void hstrlen(Connection &connection, + const StringView &key, + const StringView &field) { + connection.send("HSTRLEN %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hvals(Connection &connection, const StringView &key) { + connection.send("HVALS %b", key.data(), key.size()); +} + +// SET commands + +inline void sadd(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SADD %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void sadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SADD" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void scard(Connection &connection, const StringView &key) { + connection.send("SCARD %b", key.data(), key.size()); +} + +template +inline void sdiff(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SDIFF" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sdiffstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SDIFFSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sdiffstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SDIFFSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void sinter(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SINTER" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sinterstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SINTERSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SINTERSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +inline void sismember(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SISMEMBER %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void smembers(Connection &connection, const StringView &key) { + connection.send("SMEMBERS %b", key.data(), key.size()); +} + +inline void smove(Connection &connection, + const StringView &source, + const StringView &destination, + const StringView &member) { + connection.send("SMOVE %b %b %b", + source.data(), source.size(), + destination.data(), destination.size(), + member.data(), member.size()); +} + +inline void spop(Connection &connection, const StringView &key) { + connection.send("SPOP %b", key.data(), key.size()); +} + +inline void spop_range(Connection &connection, const StringView &key, long long count) { + connection.send("SPOP %b %lld", + key.data(), key.size(), + count); +} + +inline void srandmember(Connection &connection, const StringView &key) { + connection.send("SRANDMEMBER %b", key.data(), key.size()); +} + +inline void srandmember_range(Connection &connection, + const StringView &key, + long long count) { + connection.send("SRANDMEMBER %b %lld", + key.data(), key.size(), + count); +} + +inline void srem(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SREM %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void srem_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SREM" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void sscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("SSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +template +inline void sunion(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SUNION" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sunionstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SUNIONSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SUNIONSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +// Sorted Set commands. + +inline void bzpopmax(Connection &connection, const StringView &key, long long timeout) { + connection.send("BZPOPMAX %b %lld", key.data(), key.size(), timeout); +} + +template +void bzpopmax_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMAX" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void bzpopmin(Connection &connection, const StringView &key, long long timeout) { + connection.send("BZPOPMIN %b %lld", key.data(), key.size(), timeout); +} + +template +void bzpopmin_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMIN" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +template +void zadd_range(Connection &connection, + const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed); + +inline void zadd(Connection &connection, + const StringView &key, + const StringView &member, + double score, + UpdateType type, + bool changed) { + auto tmp = {std::make_pair(member, score)}; + + zadd_range(connection, key, tmp.begin(), tmp.end(), type, changed); +} + +inline void zcard(Connection &connection, const StringView &key) { + connection.send("ZCARD %b", key.data(), key.size()); +} + +template +inline void zcount(Connection &connection, + const StringView &key, + const Interval &interval) { + connection.send("ZCOUNT %b %s %s", + key.data(), key.size(), + interval.min().c_str(), + interval.max().c_str()); +} + +inline void zincrby(Connection &connection, + const StringView &key, + double increment, + const StringView &member) { + connection.send("ZINCRBY %b %f %b", + key.data(), key.size(), + increment, + member.data(), member.size()); +} + +inline void zinterstore(Connection &connection, + const StringView &destination, + const StringView &key, + double weight) { + connection.send("ZINTERSTORE %b 1 %b WEIGHTS %f", + destination.data(), destination.size(), + key.data(), key.size(), + weight); +} + +template +void zinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr); + +template +inline void zlexcount(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZLEXCOUNT %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zpopmax(Connection &connection, const StringView &key, long long count) { + connection.send("ZPOPMAX %b %lld", + key.data(), key.size(), + count); +} + +inline void zpopmin(Connection &connection, const StringView &key, long long count) { + connection.send("ZPOPMIN %b %lld", + key.data(), key.size(), + count); +} + +inline void zrange(Connection &connection, + const StringView &key, + long long start, + long long stop, + bool with_scores) { + if (with_scores) { + connection.send("ZRANGE %b %lld %lld WITHSCORES", + key.data(), key.size(), + start, + stop); + } else { + connection.send("ZRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); + } +} + +template +inline void zrangebylex(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); +} + +template +void zrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + if (with_scores) { + connection.send("ZRANGEBYSCORE %b %b %b WITHSCORES LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); + } else { + connection.send("ZRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); + } +} + +inline void zrank(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZRANK %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zrem(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZREM %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void zrem_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "ZREM" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void zremrangebylex(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREMRANGEBYLEX %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zremrangebyrank(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("zremrangebyrank %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +template +inline void zremrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREMRANGEBYSCORE %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zrevrange(Connection &connection, + const StringView &key, + long long start, + long long stop, + bool with_scores) { + if (with_scores) { + connection.send("ZREVRANGE %b %lld %lld WITHSCORES", + key.data(), key.size(), + start, + stop); + } else { + connection.send("ZREVRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); + } +} + +template +inline void zrevrangebylex(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREVRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); +} + +template +void zrevrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + if (with_scores) { + connection.send("ZREVRANGEBYSCORE %b %b %b WITHSCORES LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); + } else { + connection.send("ZREVRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); + } +} + +inline void zrevrank(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZREVRANK %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("ZSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void zscore(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZSCORE %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zunionstore(Connection &connection, + const StringView &destination, + const StringView &key, + double weight) { + connection.send("ZUNIONSTORE %b 1 %b WEIGHTS %f", + destination.data(), destination.size(), + key.data(), key.size(), + weight); +} + +template +void zunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr); + +// HYPERLOGLOG commands. + +inline void pfadd(Connection &connection, + const StringView &key, + const StringView &element) { + connection.send("PFADD %b %b", + key.data(), key.size(), + element.data(), element.size()); +} + +template +inline void pfadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFADD" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void pfcount(Connection &connection, const StringView &key) { + connection.send("PFCOUNT %b", key.data(), key.size()); +} + +template +inline void pfcount_range(Connection &connection, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFCOUNT" << std::make_pair(first, last); + + connection.send(args); +} + +inline void pfmerge(Connection &connection, const StringView &destination, const StringView &key) { + connection.send("PFMERGE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void pfmerge_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFMERGE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +// GEO commands. + +inline void geoadd(Connection &connection, + const StringView &key, + const std::tuple &member) { + const auto &mem = std::get<0>(member); + + connection.send("GEOADD %b %f %f %b", + key.data(), key.size(), + std::get<1>(member), + std::get<2>(member), + mem.data(), mem.size()); +} + +template +inline void geoadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOADD" << key; + + while (first != last) { + const auto &member = *first; + args << std::get<1>(member) << std::get<2>(member) << std::get<0>(member); + ++first; + } + + connection.send(args); +} + +void geodist(Connection &connection, + const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit); + +inline void geohash(Connection &connection, const StringView &key, const StringView &member) { + connection.send("GEOHASH %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void geohash_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOHASH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void geopos(Connection &connection, const StringView &key, const StringView &member) { + connection.send("GEOPOS %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void geopos_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOPOS" << key << std::make_pair(first, last); + + connection.send(args); +} + +void georadius(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +void georadius_store(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +void georadiusbymember(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +void georadiusbymember_store(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +// SCRIPTING commands. + +template +inline void eval(Connection &connection, + const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs cmd_args; + + auto keys_num = std::distance(keys_first, keys_last); + + cmd_args << "EVAL" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + connection.send(cmd_args); +} + +template +inline void evalsha(Connection &connection, + const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs cmd_args; + + auto keys_num = std::distance(keys_first, keys_last); + + cmd_args << "EVALSHA" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + connection.send(cmd_args); +} + +inline void script_exists(Connection &connection, const StringView &sha) { + connection.send("SCRIPT EXISTS %b", sha.data(), sha.size()); +} + +template +inline void script_exists_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SCRIPT" << "EXISTS" << std::make_pair(first, last); + + connection.send(args); +} + +inline void script_flush(Connection &connection) { + connection.send("SCRIPT FLUSH"); +} + +inline void script_kill(Connection &connection) { + connection.send("SCRIPT KILL"); +} + +inline void script_load(Connection &connection, const StringView &script) { + connection.send("SCRIPT LOAD %b", script.data(), script.size()); +} + +// PUBSUB commands. + +inline void psubscribe(Connection &connection, const StringView &pattern) { + connection.send("PSUBSCRIBE %b", pattern.data(), pattern.size()); +} + +template +inline void psubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("PSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "PSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void publish(Connection &connection, + const StringView &channel, + const StringView &message) { + connection.send("PUBLISH %b %b", + channel.data(), channel.size(), + message.data(), message.size()); +} + +inline void punsubscribe(Connection &connection) { + connection.send("PUNSUBSCRIBE"); +} + +inline void punsubscribe(Connection &connection, const StringView &pattern) { + connection.send("PUNSUBSCRIBE %b", pattern.data(), pattern.size()); +} + +template +inline void punsubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("PUNSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "PUNSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void subscribe(Connection &connection, const StringView &channel) { + connection.send("SUBSCRIBE %b", channel.data(), channel.size()); +} + +template +inline void subscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("SUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "SUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void unsubscribe(Connection &connection) { + connection.send("UNSUBSCRIBE"); +} + +inline void unsubscribe(Connection &connection, const StringView &channel) { + connection.send("UNSUBSCRIBE %b", channel.data(), channel.size()); +} + +template +inline void unsubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("UNSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "UNSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +// Transaction commands. + +inline void discard(Connection &connection) { + connection.send("DISCARD"); +} + +inline void exec(Connection &connection) { + connection.send("EXEC"); +} + +inline void multi(Connection &connection) { + connection.send("MULTI"); +} + +inline void unwatch(Connection &connection) { + connection.send("UNWATCH"); +} + +template +inline void unwatch_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("UNWATCH: no key specified"); + } + + CmdArgs args; + args << "UNWATCH" << std::make_pair(first, last); + + connection.send(args); +} + +inline void watch(Connection &connection, const StringView &key) { + connection.send("WATCH %b", key.data(), key.size()); +} + +template +inline void watch_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("WATCH: no key specified"); + } + + CmdArgs args; + args << "WATCH" << std::make_pair(first, last); + + connection.send(args); +} + +// Stream commands. + +inline void xack(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id) { + connection.send("XACK %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + id.data(), id.size()); +} + +template +void xack_range(Connection &connection, + const StringView &key, + const StringView &group, + Input first, + Input last) { + CmdArgs args; + args << "XACK" << key << group << std::make_pair(first, last); + + connection.send(args); +} + +template +void xadd_range(Connection &connection, + const StringView &key, + const StringView &id, + Input first, + Input last) { + CmdArgs args; + args << "XADD" << key << id << std::make_pair(first, last); + + connection.send(args); +} + +template +void xadd_maxlen_range(Connection &connection, + const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + CmdArgs args; + args << "XADD" << key << "MAXLEN"; + + if (approx) { + args << "~"; + } + + args << count << id << std::make_pair(first, last); + + connection.send(args); +} + +inline void xclaim(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer, + long long min_idle_time, + const StringView &id) { + connection.send("XCLAIM %b %b %b %lld %b", + key.data(), key.size(), + group.data(), group.size(), + consumer.data(), consumer.size(), + min_idle_time, + id.data(), id.size()); +} + +template +void xclaim_range(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer, + long long min_idle_time, + Input first, + Input last) { + CmdArgs args; + args << "XCLAIM" << key << group << consumer << min_idle_time << std::make_pair(first, last); + + connection.send(args); +} + +inline void xdel(Connection &connection, const StringView &key, const StringView &id) { + connection.send("XDEL %b %b", key.data(), key.size(), id.data(), id.size()); +} + +template +void xdel_range(Connection &connection, const StringView &key, Input first, Input last) { + CmdArgs args; + args << "XDEL" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void xgroup_create(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream) { + CmdArgs args; + args << "XGROUP" << "CREATE" << key << group << id; + + if (mkstream) { + args << "MKSTREAM"; + } + + connection.send(args); +} + +inline void xgroup_setid(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id) { + connection.send("XGROUP SETID %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + id.data(), id.size()); +} + +inline void xgroup_destroy(Connection &connection, + const StringView &key, + const StringView &group) { + connection.send("XGROUP DESTROY %b %b", + key.data(), key.size(), + group.data(), group.size()); +} + +inline void xgroup_delconsumer(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer) { + connection.send("XGROUP DELCONSUMER %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + consumer.data(), consumer.size()); +} + +inline void xlen(Connection &connection, const StringView &key) { + connection.send("XLEN %b", key.data(), key.size()); +} + +inline void xpending(Connection &connection, const StringView &key, const StringView &group) { + connection.send("XPENDING %b %b", + key.data(), key.size(), + group.data(), group.size()); +} + +inline void xpending_detail(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count) { + connection.send("XPENDING %b %b %b %b %lld", + key.data(), key.size(), + group.data(), group.size(), + start.data(), start.size(), + end.data(), end.size(), + count); +} + +inline void xpending_per_consumer(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer) { + connection.send("XPENDING %b %b %b %b %lld %b", + key.data(), key.size(), + group.data(), group.size(), + start.data(), start.size(), + end.data(), end.size(), + count, + consumer.data(), consumer.size()); +} + +inline void xrange(Connection &connection, + const StringView &key, + const StringView &start, + const StringView &end) { + connection.send("XRANGE %b %b %b", + key.data(), key.size(), + start.data(), start.size(), + end.data(), end.size()); +} + +inline void xrange_count(Connection &connection, + const StringView &key, + const StringView &start, + const StringView &end, + long long count) { + connection.send("XRANGE %b %b %b COUNT %lld", + key.data(), key.size(), + start.data(), start.size(), + end.data(), end.size(), + count); +} + +inline void xread(Connection &connection, + const StringView &key, + const StringView &id, + long long count) { + connection.send("XREAD COUNT %lld STREAMS %b %b", + count, + key.data(), key.size(), + id.data(), id.size()); +} + +template +void xread_range(Connection &connection, Input first, Input last, long long count) { + CmdArgs args; + args << "XREAD" << "COUNT" << count << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xread_block(Connection &connection, + const StringView &key, + const StringView &id, + long long timeout, + long long count) { + connection.send("XREAD COUNT %lld BLOCK %lld STREAMS %b %b", + count, + timeout, + key.data(), key.size(), + id.data(), id.size()); +} + +template +void xread_block_range(Connection &connection, + Input first, + Input last, + long long timeout, + long long count) { + CmdArgs args; + args << "XREAD" << "COUNT" << count << "BLOCK" << timeout << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xreadgroup(Connection &connection, + const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer << "COUNT" << count; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS" << key << id; + + connection.send(args); +} + +template +void xreadgroup_range(Connection &connection, + const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer << "COUNT" << count; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xreadgroup_block(Connection &connection, + const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long timeout, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer + << "COUNT" << count << "BLOCK" << timeout; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS" << key << id; + + connection.send(args); +} + +template +void xreadgroup_block_range(Connection &connection, + const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long timeout, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer + << "COUNT" << count << "BLOCK" << timeout; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xrevrange(Connection &connection, + const StringView &key, + const StringView &end, + const StringView &start) { + connection.send("XREVRANGE %b %b %b", + key.data(), key.size(), + end.data(), end.size(), + start.data(), start.size()); +} + +inline void xrevrange_count(Connection &connection, + const StringView &key, + const StringView &end, + const StringView &start, + long long count) { + connection.send("XREVRANGE %b %b %b COUNT %lld", + key.data(), key.size(), + end.data(), end.size(), + start.data(), start.size(), + count); +} + +void xtrim(Connection &connection, const StringView &key, long long count, bool approx); + +namespace detail { + +void set_bitop(CmdArgs &args, BitOp op); + +void set_update_type(CmdArgs &args, UpdateType type); + +void set_aggregation_type(CmdArgs &args, Aggregation type); + +template +void zinterstore(std::false_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZINTERSTORE" << destination << std::distance(first, last) + << std::make_pair(first, last); + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zinterstore(std::true_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZINTERSTORE" << destination << std::distance(first, last); + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + args << "WEIGHTS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zunionstore(std::false_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZUNIONSTORE" << destination << std::distance(first, last) + << std::make_pair(first, last); + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zunionstore(std::true_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZUNIONSTORE" << destination << std::distance(first, last); + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + args << "WEIGHTS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +void set_geo_unit(CmdArgs &args, GeoUnit unit); + +void set_georadius_store_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +void set_georadius_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +} + +} + +} + +} + +namespace sw { + +namespace redis { + +namespace cmd { + +template +void bitop_range(Connection &connection, + BitOp op, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + + detail::set_bitop(args, op); + + args << destination << std::make_pair(first, last); + + connection.send(args); +} + +template +void zadd_range(Connection &connection, + const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + assert(first != last); + + CmdArgs args; + + args << "ZADD" << key; + + detail::set_update_type(args, type); + + if (changed) { + args << "CH"; + } + + while (first != last) { + // Swap the pair to pair. + args << first->second << first->first; + ++first; + } + + connection.send(args); +} + +template +void zinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + assert(first != last); + + detail::zinterstore(typename IsKvPairIter::type(), + connection, + destination, + first, + last, + aggr); +} + +template +void zunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + assert(first != last); + + detail::zunionstore(typename IsKvPairIter::type(), + connection, + destination, + first, + last, + aggr); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_args.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_args.h new file mode 100644 index 000000000..0beb71e5c --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_args.h @@ -0,0 +1,180 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H + +#include +#include +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +class CmdArgs { +public: + template + CmdArgs& append(Arg &&arg); + + template + CmdArgs& append(Arg &&arg, Args &&...args); + + // All overloads of operator<< are for internal use only. + CmdArgs& operator<<(const StringView &arg); + + template ::type>::value, + int>::type = 0> + CmdArgs& operator<<(T &&arg); + + template + CmdArgs& operator<<(const std::pair &range); + + template + auto operator<<(const std::tuple &) -> + typename std::enable_if::type { + return *this; + } + + template + auto operator<<(const std::tuple &arg) -> + typename std::enable_if::type; + + const char** argv() { + return _argv.data(); + } + + const std::size_t* argv_len() { + return _argv_len.data(); + } + + std::size_t size() const { + return _argv.size(); + } + +private: + // Deep copy. + CmdArgs& _append(std::string arg); + + // Shallow copy. + CmdArgs& _append(const StringView &arg); + + // Shallow copy. + CmdArgs& _append(const char *arg); + + template ::type>::value, + int>::type = 0> + CmdArgs& _append(T &&arg) { + return operator<<(std::forward(arg)); + } + + template + CmdArgs& _append(std::true_type, const std::pair &range); + + template + CmdArgs& _append(std::false_type, const std::pair &range); + + std::vector _argv; + std::vector _argv_len; + + std::list _args; +}; + +template +inline CmdArgs& CmdArgs::append(Arg &&arg) { + return _append(std::forward(arg)); +} + +template +inline CmdArgs& CmdArgs::append(Arg &&arg, Args &&...args) { + _append(std::forward(arg)); + + return append(std::forward(args)...); +} + +inline CmdArgs& CmdArgs::operator<<(const StringView &arg) { + _argv.push_back(arg.data()); + _argv_len.push_back(arg.size()); + + return *this; +} + +template +inline CmdArgs& CmdArgs::operator<<(const std::pair &range) { + return _append(IsKvPair())>::type>(), range); +} + +template ::type>::value, + int>::type> +inline CmdArgs& CmdArgs::operator<<(T &&arg) { + return _append(std::to_string(std::forward(arg))); +} + +template +auto CmdArgs::operator<<(const std::tuple &arg) -> + typename std::enable_if::type { + operator<<(std::get(arg)); + + return operator<<(arg); +} + +inline CmdArgs& CmdArgs::_append(std::string arg) { + _args.push_back(std::move(arg)); + return operator<<(_args.back()); +} + +inline CmdArgs& CmdArgs::_append(const StringView &arg) { + return operator<<(arg); +} + +inline CmdArgs& CmdArgs::_append(const char *arg) { + return operator<<(arg); +} + +template +CmdArgs& CmdArgs::_append(std::false_type, const std::pair &range) { + auto first = range.first; + auto last = range.second; + while (first != last) { + *this << *first; + ++first; + } + + return *this; +} + +template +CmdArgs& CmdArgs::_append(std::true_type, const std::pair &range) { + auto first = range.first; + auto last = range.second; + while (first != last) { + *this << first->first << first->second; + ++first; + } + + return *this; +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_options.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_options.h new file mode 100644 index 000000000..ca766c086 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_options.h @@ -0,0 +1,211 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H + +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +enum class UpdateType { + EXIST, + NOT_EXIST, + ALWAYS +}; + +enum class InsertPosition { + BEFORE, + AFTER +}; + +enum class BoundType { + CLOSED, + OPEN, + LEFT_OPEN, + RIGHT_OPEN +}; + +// (-inf, +inf) +template +class UnboundedInterval; + +// [min, max], (min, max), (min, max], [min, max) +template +class BoundedInterval; + +// [min, +inf), (min, +inf) +template +class LeftBoundedInterval; + +// (-inf, max], (-inf, max) +template +class RightBoundedInterval; + +template <> +class UnboundedInterval { +public: + const std::string& min() const; + + const std::string& max() const; +}; + +template <> +class BoundedInterval { +public: + BoundedInterval(double min, double max, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const { + return _max; + } + +private: + std::string _min; + std::string _max; +}; + +template <> +class LeftBoundedInterval { +public: + LeftBoundedInterval(double min, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const; + +private: + std::string _min; +}; + +template <> +class RightBoundedInterval { +public: + RightBoundedInterval(double max, BoundType type); + + const std::string& min() const; + + const std::string& max() const { + return _max; + } + +private: + std::string _max; +}; + +template <> +class UnboundedInterval { +public: + const std::string& min() const; + + const std::string& max() const; +}; + +template <> +class BoundedInterval { +public: + BoundedInterval(const std::string &min, const std::string &max, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const { + return _max; + } + +private: + std::string _min; + std::string _max; +}; + +template <> +class LeftBoundedInterval { +public: + LeftBoundedInterval(const std::string &min, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const; + +private: + std::string _min; +}; + +template <> +class RightBoundedInterval { +public: + RightBoundedInterval(const std::string &max, BoundType type); + + const std::string& min() const; + + const std::string& max() const { + return _max; + } + +private: + std::string _max; +}; + +struct LimitOptions { + long long offset = 0; + long long count = -1; +}; + +enum class Aggregation { + SUM, + MIN, + MAX +}; + +enum class BitOp { + AND, + OR, + XOR, + NOT +}; + +enum class GeoUnit { + M, + KM, + MI, + FT +}; + +template +struct WithCoord : TupleWithType, T> {}; + +template +struct WithDist : TupleWithType {}; + +template +struct WithHash : TupleWithType {}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection.h new file mode 100644 index 000000000..340375561 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection.h @@ -0,0 +1,237 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_H +#define SEWENEW_REDISPLUSPLUS_CONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "reply.h" +#include "utils.h" +#include "tls.h" + +namespace sw { + +namespace redis { + +enum class ConnectionType { + TCP = 0, + UNIX +}; + +struct ConnectionOptions { +public: + ConnectionOptions() = default; + + explicit ConnectionOptions(const std::string &uri); + + ConnectionOptions(const ConnectionOptions &) = default; + ConnectionOptions& operator=(const ConnectionOptions &) = default; + + ConnectionOptions(ConnectionOptions &&) = default; + ConnectionOptions& operator=(ConnectionOptions &&) = default; + + ~ConnectionOptions() = default; + + ConnectionType type = ConnectionType::TCP; + + std::string host; + + int port = 6379; + + std::string path; + + std::string user = "default"; + + std::string password; + + int db = 0; + + bool keep_alive = false; + + std::chrono::milliseconds connect_timeout{0}; + + std::chrono::milliseconds socket_timeout{0}; + + tls::TlsOptions tls; + + // `readonly` is only used for reading from a slave node in Redis Cluster mode. + // Client code should never manually set/get it. This member might be removed in the future. + bool readonly = false; + +private: + ConnectionOptions _parse_uri(const std::string &uri) const; + + auto _split_uri(const std::string &uri) const + -> std::tuple; + + auto _split_path(const std::string &path) const + -> std::tuple; + + void _parse_parameters(const std::string ¶meter_string, + ConnectionOptions &opts) const; + + void _set_option(const std::string &key, const std::string &val, ConnectionOptions &opts) const; + + bool _parse_bool_option(const std::string &str) const; + + std::chrono::milliseconds _parse_timeout_option(const std::string &str) const; + + std::vector _split(const std::string &str, const std::string &delimiter) const; + + void _set_auth_opts(const std::string &auth, ConnectionOptions &opts) const; + + void _set_tcp_opts(const std::string &path, ConnectionOptions &opts) const; + + void _set_unix_opts(const std::string &path, ConnectionOptions &opts) const; +}; + +class CmdArgs; + +class Connection { +public: + explicit Connection(const ConnectionOptions &opts); + + Connection(const Connection &) = delete; + Connection& operator=(const Connection &) = delete; + + Connection(Connection &&) = default; + Connection& operator=(Connection &&) = default; + + ~Connection() = default; + + // Check if the connection is broken. Client needs to do this check + // before sending some command to the connection. If it's broken, + // client needs to reconnect it. + bool broken() const noexcept { + return _ctx->err != REDIS_OK; + } + + void reset() noexcept { + _ctx->err = 0; + } + + void invalidate() noexcept { + _ctx->err = REDIS_ERR; + } + + void reconnect(); + + auto create_time() const + -> std::chrono::time_point { + return _create_time; + } + + auto last_active() const + -> std::chrono::time_point { + return _last_active; + } + + template + void send(const char *format, Args &&...args); + + void send(int argc, const char **argv, const std::size_t *argv_len); + + void send(CmdArgs &args); + + ReplyUPtr recv(bool handle_error_reply = true); + + const ConnectionOptions& options() const { + return _opts; + } + + friend void swap(Connection &lhs, Connection &rhs) noexcept; + +private: + class Connector; + + struct ContextDeleter { + void operator()(redisContext *context) const { + if (context != nullptr) { + redisFree(context); + } + }; + }; + + using ContextUPtr = std::unique_ptr; + + void _set_options(); + + void _auth(); + + void _select_db(); + + void _enable_readonly(); + + redisContext* _context(); + + ContextUPtr _ctx; + + // The time that the connection is created. + std::chrono::time_point _create_time{}; + + // The time that the connection is created or the time that + // the connection is recently used, i.e. `_context()` is called. + std::chrono::time_point _last_active{}; + + ConnectionOptions _opts; + + // TODO: define _tls_ctx before _ctx + tls::TlsContextUPtr _tls_ctx; +}; + +using ConnectionSPtr = std::shared_ptr; + +enum class Role { + MASTER, + SLAVE +}; + +// Inline implementaions. + +template +inline void Connection::send(const char *format, Args &&...args) { + auto ctx = _context(); + + assert(ctx != nullptr); + + if (redisAppendCommand(ctx, + format, + std::forward(args)...) != REDIS_OK) { + throw_error(*ctx, "Failed to send command"); + } + + assert(!broken()); +} + +inline redisContext* Connection::_context() { + _last_active = std::chrono::steady_clock::now(); + + return _ctx.get(); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection_pool.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection_pool.h new file mode 100644 index 000000000..30ba5daa2 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection_pool.h @@ -0,0 +1,182 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H +#define SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H + +#include +#include +#include +#include +#include +#include +#include "connection.h" +#include "sentinel.h" + +namespace sw { + +namespace redis { + +struct ConnectionPoolOptions { + // Max number of connections, including both in-use and idle ones. + std::size_t size = 1; + + // Max time to wait for a connection. 0ms means client waits forever. + std::chrono::milliseconds wait_timeout{0}; + + // Max lifetime of a connection. 0ms means we never expire the connection. + std::chrono::milliseconds connection_lifetime{0}; + + // Max idle time of a connection. 0ms means we never expire the connection. + std::chrono::milliseconds connection_idle_time{0}; +}; + +class ConnectionPool { +public: + ConnectionPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + ConnectionPool(SimpleSentinel sentinel, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + ConnectionPool() = default; + + ConnectionPool(ConnectionPool &&that); + ConnectionPool& operator=(ConnectionPool &&that); + + ConnectionPool(const ConnectionPool &) = delete; + ConnectionPool& operator=(const ConnectionPool &) = delete; + + ~ConnectionPool() = default; + + // Fetch a connection from pool. + Connection fetch(); + + ConnectionOptions connection_options(); + + void release(Connection connection); + + // Create a new connection. + Connection create(); + + ConnectionPool clone(); + +private: + void _move(ConnectionPool &&that); + + // NOT thread-safe + Connection _create(); + + Connection _create(SimpleSentinel &sentinel, const ConnectionOptions &opts, bool locked); + + Connection _fetch(); + + void _wait_for_connection(std::unique_lock &lock); + + bool _need_reconnect(const Connection &connection, + const std::chrono::milliseconds &connection_lifetime, + const std::chrono::milliseconds &connection_idle_time) const; + + void _update_connection_opts(const std::string &host, int port) { + _opts.host = host; + _opts.port = port; + } + + bool _role_changed(const ConnectionOptions &opts) const { + return opts.port != _opts.port || opts.host != _opts.host; + } + + ConnectionOptions _opts; + + ConnectionPoolOptions _pool_opts; + + std::deque _pool; + + std::size_t _used_connections = 0; + + std::mutex _mutex; + + std::condition_variable _cv; + + SimpleSentinel _sentinel; +}; + +using ConnectionPoolSPtr = std::shared_ptr; + +class SafeConnection { +public: + explicit SafeConnection(ConnectionPool &pool) : _pool(pool), _connection(_pool.fetch()) { + assert(!_connection.broken()); + } + + SafeConnection(const SafeConnection &) = delete; + SafeConnection& operator=(const SafeConnection &) = delete; + + SafeConnection(SafeConnection &&) = delete; + SafeConnection& operator=(SafeConnection &&) = delete; + + ~SafeConnection() { + _pool.release(std::move(_connection)); + } + + Connection& connection() { + return _connection; + } + +private: + ConnectionPool &_pool; + Connection _connection; +}; + +// NOTE: This class is similar to `SafeConnection`. +// The difference is that `SafeConnection` tries to avoid copying a std::shared_ptr. +class GuardedConnection { +public: + explicit GuardedConnection(const ConnectionPoolSPtr &pool) : _pool(pool), + _connection(_pool->fetch()) { + assert(!_connection.broken()); + } + + GuardedConnection(const GuardedConnection &) = delete; + GuardedConnection& operator=(const GuardedConnection &) = delete; + + GuardedConnection(GuardedConnection &&) = default; + GuardedConnection& operator=(GuardedConnection &&) = default; + + ~GuardedConnection() { + // If `GuardedConnection` has been moved, `_pool` will be nullptr. + if (_pool) { + _pool->release(std::move(_connection)); + } + } + + Connection& connection() { + return _connection; + } + +private: + ConnectionPoolSPtr _pool; + Connection _connection; +}; + +using GuardedConnectionSPtr = std::shared_ptr; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cxx_utils.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cxx_utils.h new file mode 100644 index 000000000..dce51abe9 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cxx_utils.h @@ -0,0 +1,46 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CXX_UTILS_H +#define SEWENEW_REDISPLUSPLUS_CXX_UTILS_H + +#include +#include +#include + +#define REDIS_PLUS_PLUS_HAS_OPTIONAL + +#define REDIS_PLUS_PLUS_HAS_VARIANT + +namespace sw { + +namespace redis { + +using StringView = std::string_view; + +template +using Optional = std::optional; + +template +using Variant = std::variant; + +using Monostate = std::monostate; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CXX_UTILS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/errors.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/errors.h new file mode 100644 index 000000000..7cc19d2f1 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/errors.h @@ -0,0 +1,166 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ERRORS_H +#define SEWENEW_REDISPLUSPLUS_ERRORS_H + +#include +#include +#include + +namespace sw { + +namespace redis { + +enum ReplyErrorType { + ERR, + MOVED, + ASK +}; + +class Error : public std::exception { +public: + explicit Error(const std::string &msg) : _msg(msg) {} + + Error(const Error &) = default; + Error& operator=(const Error &) = default; + + Error(Error &&) = default; + Error& operator=(Error &&) = default; + + virtual ~Error() override = default; + + virtual const char* what() const noexcept override { + return _msg.data(); + } + +private: + std::string _msg; +}; + +class IoError : public Error { +public: + explicit IoError(const std::string &msg) : Error(msg) {} + + IoError(const IoError &) = default; + IoError& operator=(const IoError &) = default; + + IoError(IoError &&) = default; + IoError& operator=(IoError &&) = default; + + virtual ~IoError() override = default; +}; + +class TimeoutError : public IoError { +public: + explicit TimeoutError(const std::string &msg) : IoError(msg) {} + + TimeoutError(const TimeoutError &) = default; + TimeoutError& operator=(const TimeoutError &) = default; + + TimeoutError(TimeoutError &&) = default; + TimeoutError& operator=(TimeoutError &&) = default; + + virtual ~TimeoutError() override = default; +}; + +class ClosedError : public Error { +public: + explicit ClosedError(const std::string &msg) : Error(msg) {} + + ClosedError(const ClosedError &) = default; + ClosedError& operator=(const ClosedError &) = default; + + ClosedError(ClosedError &&) = default; + ClosedError& operator=(ClosedError &&) = default; + + virtual ~ClosedError() override = default; +}; + +class ProtoError : public Error { +public: + explicit ProtoError(const std::string &msg) : Error(msg) {} + + ProtoError(const ProtoError &) = default; + ProtoError& operator=(const ProtoError &) = default; + + ProtoError(ProtoError &&) = default; + ProtoError& operator=(ProtoError &&) = default; + + virtual ~ProtoError() override = default; +}; + +class OomError : public Error { +public: + explicit OomError(const std::string &msg) : Error(msg) {} + + OomError(const OomError &) = default; + OomError& operator=(const OomError &) = default; + + OomError(OomError &&) = default; + OomError& operator=(OomError &&) = default; + + virtual ~OomError() override = default; +}; + +class ReplyError : public Error { +public: + explicit ReplyError(const std::string &msg) : Error(msg) {} + + ReplyError(const ReplyError &) = default; + ReplyError& operator=(const ReplyError &) = default; + + ReplyError(ReplyError &&) = default; + ReplyError& operator=(ReplyError &&) = default; + + virtual ~ReplyError() override = default; +}; + +class WatchError : public Error { +public: + explicit WatchError() : Error("Watched key has been modified") {} + + WatchError(const WatchError &) = default; + WatchError& operator=(const WatchError &) = default; + + WatchError(WatchError &&) = default; + WatchError& operator=(WatchError &&) = default; + + virtual ~WatchError() override = default; +}; + + +// MovedError and AskError are defined in shards.h +class MovedError; + +class AskError; + +void throw_error(const redisContext &context, const std::string &err_info); + +void throw_error(const redisReply &reply); + +template +inline void range_check(const char *cmd, Input first, Input last) { + if (first == last) { + throw Error(std::string(cmd) + ": no key specified"); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ERRORS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/pipeline.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/pipeline.h new file mode 100644 index 000000000..52b01253f --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/pipeline.h @@ -0,0 +1,49 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_PIPELINE_H +#define SEWENEW_REDISPLUSPLUS_PIPELINE_H + +#include +#include +#include "connection.h" + +namespace sw { + +namespace redis { + +class PipelineImpl { +public: + template + void command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + } + + std::vector exec(Connection &connection, std::size_t cmd_num); + + void discard(Connection &connection, std::size_t /*cmd_num*/) { + // Reconnect to Redis to discard all commands. + connection.reconnect(); + } +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_PIPELINE_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.h new file mode 100644 index 000000000..e663c7423 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.h @@ -0,0 +1,2013 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H +#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H + +#include +#include +#include +#include +#include +#include "connection.h" +#include "connection_pool.h" +#include "utils.h" +#include "reply.h" +#include "command.h" +#include "redis.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class QueuedReplies; + +// If any command throws, QueuedRedis resets the connection, and becomes invalid. +// In this case, the only thing we can do is to destory the QueuedRedis object. +template +class QueuedRedis { +public: + QueuedRedis(QueuedRedis &&) = default; + QueuedRedis& operator=(QueuedRedis &&) = default; + + // When it destructs, the underlying *Connection* will be closed or return to pool, + // and any command that has NOT been executed will be ignored. + ~QueuedRedis(); + + Redis redis(); + + template + auto command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, + QueuedRedis&>::type; + + template + QueuedRedis& command(const StringView &cmd_name, Args &&...args); + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, QueuedRedis&>::type; + + QueuedReplies exec(); + + void discard(); + + // CONNECTION commands. + + QueuedRedis& auth(const StringView &password) { + return command(cmd::auth, password); + } + + QueuedRedis& auth(const StringView &user, const StringView &password) { + return command( + cmd::auth, user, password); + } + + QueuedRedis& echo(const StringView &msg) { + return command(cmd::echo, msg); + } + + QueuedRedis& ping() { + return command(cmd::ping); + } + + QueuedRedis& ping(const StringView &msg) { + return command(cmd::ping, msg); + } + + // We DO NOT support the QUIT command. See *Redis::quit* doc for details. + // + // QueuedRedis& quit(); + + QueuedRedis& select(long long idx) { + return command(cmd::select, idx); + } + + QueuedRedis& swapdb(long long idx1, long long idx2) { + return command(cmd::swapdb, idx1, idx2); + } + + // SERVER commands. + + QueuedRedis& bgrewriteaof() { + return command(cmd::bgrewriteaof); + } + + QueuedRedis& bgsave() { + return command(cmd::bgsave); + } + + QueuedRedis& dbsize() { + return command(cmd::dbsize); + } + + QueuedRedis& flushall(bool async = false) { + return command(cmd::flushall, async); + } + + QueuedRedis& flushdb(bool async = false) { + return command(cmd::flushdb, async); + } + + QueuedRedis& info() { + return command(cmd::info); + } + + QueuedRedis& info(const StringView §ion) { + return command(cmd::info, section); + } + + QueuedRedis& lastsave() { + return command(cmd::lastsave); + } + + QueuedRedis& save() { + return command(cmd::save); + } + + // KEY commands. + + QueuedRedis& del(const StringView &key) { + return command(cmd::del, key); + } + + template + QueuedRedis& del(Input first, Input last) { + range_check("DEL", first, last); + + return command(cmd::del_range, first, last); + } + + template + QueuedRedis& del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + QueuedRedis& dump(const StringView &key) { + return command(cmd::dump, key); + } + + QueuedRedis& exists(const StringView &key) { + return command(cmd::exists, key); + } + + template + QueuedRedis& exists(Input first, Input last) { + range_check("EXISTS", first, last); + + return command(cmd::exists_range, first, last); + } + + template + QueuedRedis& exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + QueuedRedis& expire(const StringView &key, long long timeout) { + return command(cmd::expire, key, timeout); + } + + QueuedRedis& expire(const StringView &key, + const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); + } + + QueuedRedis& expireat(const StringView &key, long long timestamp) { + return command(cmd::expireat, key, timestamp); + } + + QueuedRedis& expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); + } + + QueuedRedis& keys(const StringView &pattern) { + return command(cmd::keys, pattern); + } + + QueuedRedis& move(const StringView &key, long long db) { + return command(cmd::move, key, db); + } + + QueuedRedis& persist(const StringView &key) { + return command(cmd::persist, key); + } + + QueuedRedis& pexpire(const StringView &key, long long timeout) { + return command(cmd::pexpire, key, timeout); + } + + QueuedRedis& pexpire(const StringView &key, + const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); + } + + QueuedRedis& pexpireat(const StringView &key, long long timestamp) { + return command(cmd::pexpireat, key, timestamp); + } + + QueuedRedis& pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); + } + + QueuedRedis& pttl(const StringView &key) { + return command(cmd::pttl, key); + } + + QueuedRedis& randomkey() { + return command(cmd::randomkey); + } + + QueuedRedis& rename(const StringView &key, const StringView &newkey) { + return command(cmd::rename, key, newkey); + } + + QueuedRedis& renamenx(const StringView &key, const StringView &newkey) { + return command(cmd::renamenx, key, newkey); + } + + QueuedRedis& restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false) { + return command(cmd::restore, key, val, ttl, replace); + } + + QueuedRedis& restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false) { + return restore(key, val, ttl.count(), replace); + } + + // TODO: sort + + QueuedRedis& scan(long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::scan, cursor, pattern, count); + } + + QueuedRedis& scan(long long cursor) { + return scan(cursor, "*", 10); + } + + QueuedRedis& scan(long long cursor, + const StringView &pattern) { + return scan(cursor, pattern, 10); + } + + QueuedRedis& scan(long long cursor, + long long count) { + return scan(cursor, "*", count); + } + + QueuedRedis& touch(const StringView &key) { + return command(cmd::touch, key); + } + + template + QueuedRedis& touch(Input first, Input last) { + range_check("TOUCH", first, last); + + return command(cmd::touch_range, first, last); + } + + template + QueuedRedis& touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + QueuedRedis& ttl(const StringView &key) { + return command(cmd::ttl, key); + } + + QueuedRedis& type(const StringView &key) { + return command(cmd::type, key); + } + + QueuedRedis& unlink(const StringView &key) { + return command(cmd::unlink, key); + } + + template + QueuedRedis& unlink(Input first, Input last) { + range_check("UNLINK", first, last); + + return command(cmd::unlink_range, first, last); + } + + template + QueuedRedis& unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + QueuedRedis& wait(long long numslaves, long long timeout) { + return command(cmd::wait, numslaves, timeout); + } + + QueuedRedis& wait(long long numslaves, const std::chrono::milliseconds &timeout) { + return wait(numslaves, timeout.count()); + } + + // STRING commands. + + QueuedRedis& append(const StringView &key, const StringView &str) { + return command(cmd::append, key, str); + } + + QueuedRedis& bitcount(const StringView &key, + long long start = 0, + long long end = -1) { + return command(cmd::bitcount, key, start, end); + } + + QueuedRedis& bitop(BitOp op, + const StringView &destination, + const StringView &key) { + return command(cmd::bitop, op, destination, key); + } + + template + QueuedRedis& bitop(BitOp op, + const StringView &destination, + Input first, + Input last) { + range_check("BITOP", first, last); + + return command(cmd::bitop_range, op, destination, first, last); + } + + template + QueuedRedis& bitop(BitOp op, + const StringView &destination, + std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + QueuedRedis& bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1) { + return command(cmd::bitpos, key, bit, start, end); + } + + QueuedRedis& decr(const StringView &key) { + return command(cmd::decr, key); + } + + QueuedRedis& decrby(const StringView &key, long long decrement) { + return command(cmd::decrby, key, decrement); + } + + QueuedRedis& get(const StringView &key) { + return command(cmd::get, key); + } + + QueuedRedis& getbit(const StringView &key, long long offset) { + return command(cmd::getbit, key, offset); + } + + QueuedRedis& getrange(const StringView &key, long long start, long long end) { + return command(cmd::getrange, key, start, end); + } + + QueuedRedis& getset(const StringView &key, const StringView &val) { + return command(cmd::getset, key, val); + } + + QueuedRedis& incr(const StringView &key) { + return command(cmd::incr, key); + } + + QueuedRedis& incrby(const StringView &key, long long increment) { + return command(cmd::incrby, key, increment); + } + + QueuedRedis& incrbyfloat(const StringView &key, double increment) { + return command(cmd::incrbyfloat, key, increment); + } + + template + QueuedRedis& mget(Input first, Input last) { + range_check("MGET", first, last); + + return command(cmd::mget, first, last); + } + + template + QueuedRedis& mget(std::initializer_list il) { + return mget(il.begin(), il.end()); + } + + template + QueuedRedis& mset(Input first, Input last) { + range_check("MSET", first, last); + + return command(cmd::mset, first, last); + } + + template + QueuedRedis& mset(std::initializer_list il) { + return mset(il.begin(), il.end()); + } + + template + QueuedRedis& msetnx(Input first, Input last) { + range_check("MSETNX", first, last); + + return command(cmd::msetnx, first, last); + } + + template + QueuedRedis& msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + QueuedRedis& psetex(const StringView &key, + long long ttl, + const StringView &val) { + return command(cmd::psetex, key, ttl, val); + } + + QueuedRedis& psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); + } + + QueuedRedis& set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS) { + _set_cmd_indexes.push_back(_cmd_num); + + return command(cmd::set, key, val, ttl.count(), type); + } + + QueuedRedis& setex(const StringView &key, + long long ttl, + const StringView &val) { + return command(cmd::setex, key, ttl, val); + } + + QueuedRedis& setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + return setex(key, ttl.count(), val); + } + + QueuedRedis& setnx(const StringView &key, const StringView &val) { + return command(cmd::setnx, key, val); + } + + QueuedRedis& setrange(const StringView &key, + long long offset, + const StringView &val) { + return command(cmd::setrange, key, offset, val); + } + + QueuedRedis& strlen(const StringView &key) { + return command(cmd::strlen, key); + } + + // LIST commands. + + QueuedRedis& blpop(const StringView &key, long long timeout) { + return command(cmd::blpop, key, timeout); + } + + QueuedRedis& blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(key, timeout.count()); + } + + template + QueuedRedis& blpop(Input first, Input last, long long timeout) { + range_check("BLPOP", first, last); + + return command(cmd::blpop_range, first, last, timeout); + } + + template + QueuedRedis& blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(first, last, timeout.count()); + } + + template + QueuedRedis& blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + QueuedRedis& brpop(const StringView &key, long long timeout) { + return command(cmd::brpop, key, timeout); + } + + QueuedRedis& brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(key, timeout.count()); + } + + template + QueuedRedis& brpop(Input first, Input last, long long timeout) { + range_check("BRPOP", first, last); + + return command(cmd::brpop_range, first, last, timeout); + } + + template + QueuedRedis& brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(first, last, timeout.count()); + } + + template + QueuedRedis& brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + QueuedRedis& brpoplpush(const StringView &source, + const StringView &destination, + long long timeout) { + return command(cmd::brpoplpush, source, destination, timeout); + } + + QueuedRedis& brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpoplpush(source, destination, timeout.count()); + } + + QueuedRedis& lindex(const StringView &key, long long index) { + return command(cmd::lindex, key, index); + } + + QueuedRedis& linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val) { + return command(cmd::linsert, key, position, pivot, val); + } + + QueuedRedis& llen(const StringView &key) { + return command(cmd::llen, key); + } + + QueuedRedis& lpop(const StringView &key) { + return command(cmd::lpop, key); + } + + QueuedRedis& lpush(const StringView &key, const StringView &val) { + return command(cmd::lpush, key, val); + } + + template + QueuedRedis& lpush(const StringView &key, Input first, Input last) { + range_check("LPUSH", first, last); + + return command(cmd::lpush_range, key, first, last); + } + + template + QueuedRedis& lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + QueuedRedis& lpushx(const StringView &key, const StringView &val) { + return command(cmd::lpushx, key, val); + } + + QueuedRedis& lrange(const StringView &key, + long long start, + long long stop) { + return command(cmd::lrange, key, start, stop); + } + + QueuedRedis& lrem(const StringView &key, long long count, const StringView &val) { + return command(cmd::lrem, key, count, val); + } + + QueuedRedis& lset(const StringView &key, long long index, const StringView &val) { + return command(cmd::lset, key, index, val); + } + + QueuedRedis& ltrim(const StringView &key, long long start, long long stop) { + return command(cmd::ltrim, key, start, stop); + } + + QueuedRedis& rpop(const StringView &key) { + return command(cmd::rpop, key); + } + + QueuedRedis& rpoplpush(const StringView &source, const StringView &destination) { + return command(cmd::rpoplpush, source, destination); + } + + QueuedRedis& rpush(const StringView &key, const StringView &val) { + return command(cmd::rpush, key, val); + } + + template + QueuedRedis& rpush(const StringView &key, Input first, Input last) { + range_check("RPUSH", first, last); + + return command(cmd::rpush_range, key, first, last); + } + + template + QueuedRedis& rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + QueuedRedis& rpushx(const StringView &key, const StringView &val) { + return command(cmd::rpushx, key, val); + } + + // HASH commands. + + QueuedRedis& hdel(const StringView &key, const StringView &field) { + return command(cmd::hdel, key, field); + } + + template + QueuedRedis& hdel(const StringView &key, Input first, Input last) { + range_check("HDEL", first, last); + + return command(cmd::hdel_range, key, first, last); + } + + template + QueuedRedis& hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + QueuedRedis& hexists(const StringView &key, const StringView &field) { + return command(cmd::hexists, key, field); + } + + QueuedRedis& hget(const StringView &key, const StringView &field) { + return command(cmd::hget, key, field); + } + + QueuedRedis& hgetall(const StringView &key) { + return command(cmd::hgetall, key); + } + + QueuedRedis& hincrby(const StringView &key, + const StringView &field, + long long increment) { + return command(cmd::hincrby, key, field, increment); + } + + QueuedRedis& hincrbyfloat(const StringView &key, + const StringView &field, + double increment) { + return command(cmd::hincrbyfloat, key, field, increment); + } + + QueuedRedis& hkeys(const StringView &key) { + return command(cmd::hkeys, key); + } + + QueuedRedis& hlen(const StringView &key) { + return command(cmd::hlen, key); + } + + template + QueuedRedis& hmget(const StringView &key, Input first, Input last) { + range_check("HMGET", first, last); + + return command(cmd::hmget, key, first, last); + } + + template + QueuedRedis& hmget(const StringView &key, std::initializer_list il) { + return hmget(key, il.begin(), il.end()); + } + + template + QueuedRedis& hmset(const StringView &key, Input first, Input last) { + range_check("HMSET", first, last); + + return command(cmd::hmset, key, first, last); + } + + template + QueuedRedis& hmset(const StringView &key, std::initializer_list il) { + return hmset(key, il.begin(), il.end()); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::hscan, key, cursor, pattern, count); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return hscan(key, cursor, pattern, 10); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + long long count) { + return hscan(key, cursor, "*", count); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor) { + return hscan(key, cursor, "*", 10); + } + + QueuedRedis& hset(const StringView &key, const StringView &field, const StringView &val) { + return command(cmd::hset, key, field, val); + } + + QueuedRedis& hset(const StringView &key, const std::pair &item) { + return hset(key, item.first, item.second); + } + + template + auto hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("HSET", first, last); + + return command(cmd::hset_range, key, first, last); + } + + template + QueuedRedis& hset(const StringView &key, std::initializer_list il) { + return hset(key, il.begin(), il.end()); + } + + QueuedRedis& hsetnx(const StringView &key, const StringView &field, const StringView &val) { + return command(cmd::hsetnx, key, field, val); + } + + QueuedRedis& hsetnx(const StringView &key, const std::pair &item) { + return hsetnx(key, item.first, item.second); + } + + QueuedRedis& hstrlen(const StringView &key, const StringView &field) { + return command(cmd::hstrlen, key, field); + } + + QueuedRedis& hvals(const StringView &key) { + return command(cmd::hvals, key); + } + + // SET commands. + + QueuedRedis& sadd(const StringView &key, const StringView &member) { + return command(cmd::sadd, key, member); + } + + template + QueuedRedis& sadd(const StringView &key, Input first, Input last) { + range_check("SADD", first, last); + + return command(cmd::sadd_range, key, first, last); + } + + template + QueuedRedis& sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + QueuedRedis& scard(const StringView &key) { + return command(cmd::scard, key); + } + + template + QueuedRedis& sdiff(Input first, Input last) { + range_check("SDIFF", first, last); + + return command(cmd::sdiff, first, last); + } + + template + QueuedRedis& sdiff(std::initializer_list il) { + return sdiff(il.begin(), il.end()); + } + + QueuedRedis& sdiffstore(const StringView &destination, const StringView &key) { + return command(cmd::sdiffstore, destination, key); + } + + template + QueuedRedis& sdiffstore(const StringView &destination, + Input first, + Input last) { + range_check("SDIFFSTORE", first, last); + + return command(cmd::sdiffstore_range, destination, first, last); + } + + template + QueuedRedis& sdiffstore(const StringView &destination, std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + QueuedRedis& sinter(Input first, Input last) { + range_check("SINTER", first, last); + + return command(cmd::sinter, first, last); + } + + template + QueuedRedis& sinter(std::initializer_list il) { + return sinter(il.begin(), il.end()); + } + + QueuedRedis& sinterstore(const StringView &destination, const StringView &key) { + return command(cmd::sinterstore, destination, key); + } + + template + QueuedRedis& sinterstore(const StringView &destination, + Input first, + Input last) { + range_check("SINTERSTORE", first, last); + + return command(cmd::sinterstore_range, destination, first, last); + } + + template + QueuedRedis& sinterstore(const StringView &destination, std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + QueuedRedis& sismember(const StringView &key, const StringView &member) { + return command(cmd::sismember, key, member); + } + + QueuedRedis& smembers(const StringView &key) { + return command(cmd::smembers, key); + } + + QueuedRedis& smove(const StringView &source, + const StringView &destination, + const StringView &member) { + return command(cmd::smove, source, destination, member); + } + + QueuedRedis& spop(const StringView &key) { + return command(cmd::spop, key); + } + + QueuedRedis& spop(const StringView &key, long long count) { + return command(cmd::spop_range, key, count); + } + + QueuedRedis& srandmember(const StringView &key) { + return command(cmd::srandmember, key); + } + + QueuedRedis& srandmember(const StringView &key, long long count) { + return command(cmd::srandmember_range, key, count); + } + + QueuedRedis& srem(const StringView &key, const StringView &member) { + return command(cmd::srem, key, member); + } + + template + QueuedRedis& srem(const StringView &key, Input first, Input last) { + range_check("SREM", first, last); + + return command(cmd::srem_range, key, first, last); + } + + template + QueuedRedis& srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::sscan, key, cursor, pattern, count); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return sscan(key, cursor, pattern, 10); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + long long count) { + return sscan(key, cursor, "*", count); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor) { + return sscan(key, cursor, "*", 10); + } + + template + QueuedRedis& sunion(Input first, Input last) { + range_check("SUNION", first, last); + + return command(cmd::sunion, first, last); + } + + template + QueuedRedis& sunion(std::initializer_list il) { + return sunion(il.begin(), il.end()); + } + + QueuedRedis& sunionstore(const StringView &destination, const StringView &key) { + return command(cmd::sunionstore, destination, key); + } + + template + QueuedRedis& sunionstore(const StringView &destination, Input first, Input last) { + range_check("SUNIONSTORE", first, last); + + return command(cmd::sunionstore_range, destination, first, last); + } + + template + QueuedRedis& sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + QueuedRedis& bzpopmax(const StringView &key, long long timeout) { + return command(cmd::bzpopmax, key, timeout); + } + + QueuedRedis& bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(key, timeout.count()); + } + + template + QueuedRedis& bzpopmax(Input first, Input last, long long timeout) { + range_check("BZPOPMAX", first, last); + + return command(cmd::bzpopmax_range, first, last, timeout); + } + + template + QueuedRedis& bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(first, last, timeout.count()); + } + + template + QueuedRedis& bzpopmax(std::initializer_list il, long long timeout) { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(il.begin(), il.end(), timeout); + } + + QueuedRedis& bzpopmin(const StringView &key, long long timeout) { + return command(cmd::bzpopmin, key, timeout); + } + + QueuedRedis& bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(key, timeout.count()); + } + + template + QueuedRedis& bzpopmin(Input first, Input last, long long timeout) { + range_check("BZPOPMIN", first, last); + + return command(cmd::bzpopmin_range, first, last, timeout); + } + + template + QueuedRedis& bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(first, last, timeout.count()); + } + + template + QueuedRedis& bzpopmin(std::initializer_list il, long long timeout) { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + QueuedRedis& zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return command(cmd::zadd, key, member, score, type, changed); + } + + template + QueuedRedis& zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + range_check("ZADD", first, last); + + return command(cmd::zadd_range, key, first, last, type, changed); + } + + QueuedRedis& zcard(const StringView &key) { + return command(cmd::zcard, key); + } + + template + QueuedRedis& zcount(const StringView &key, const Interval &interval) { + return command(cmd::zcount, key, interval); + } + + QueuedRedis& zincrby(const StringView &key, double increment, const StringView &member) { + return command(cmd::zincrby, key, increment, member); + } + + QueuedRedis& zinterstore(const StringView &destination, + const StringView &key, + double weight) { + return command(cmd::zinterstore, destination, key, weight); + } + + template + QueuedRedis& zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM) { + range_check("ZINTERSTORE", first, last); + + return command(cmd::zinterstore_range, destination, first, last, type); + } + + template + QueuedRedis& zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + QueuedRedis& zlexcount(const StringView &key, const Interval &interval) { + return command(cmd::zlexcount, key, interval); + } + + QueuedRedis& zpopmax(const StringView &key) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::zpopmax, key, 1); + } + + QueuedRedis& zpopmax(const StringView &key, long long count) { + return command(cmd::zpopmax, key, count); + } + + QueuedRedis& zpopmin(const StringView &key) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::zpopmin, key, 1); + } + + QueuedRedis& zpopmin(const StringView &key, long long count) { + return command(cmd::zpopmin, key, count); + } + + // NOTE: *QueuedRedis::zrange*'s parameters are different from *Redis::zrange*. + // *Redis::zrange* is overloaded by the output iterator, however, there's no such + // iterator in *QueuedRedis::zrange*. So we have to use an extra parameter: *with_scores*, + // to decide whether we should send *WITHSCORES* option to Redis. This also applies to + // other commands with the *WITHSCORES* option, e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, + // *ZREVRANGEBYSCORE*. + QueuedRedis& zrange(const StringView &key, + long long start, + long long stop, + bool with_scores = false) { + return command(cmd::zrange, key, start, stop, with_scores); + } + + template + QueuedRedis& zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return command(cmd::zrangebylex, key, interval, opts); + } + + template + QueuedRedis& zrangebylex(const StringView &key, const Interval &interval) { + return zrangebylex(key, interval, {}); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores = false) { + return command(cmd::zrangebyscore, key, interval, opts, with_scores); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrangebyscore(const StringView &key, + const Interval &interval, + bool with_scores = false) { + return zrangebyscore(key, interval, {}, with_scores); + } + + QueuedRedis& zrank(const StringView &key, const StringView &member) { + return command(cmd::zrank, key, member); + } + + QueuedRedis& zrem(const StringView &key, const StringView &member) { + return command(cmd::zrem, key, member); + } + + template + QueuedRedis& zrem(const StringView &key, Input first, Input last) { + range_check("ZREM", first, last); + + return command(cmd::zrem_range, key, first, last); + } + + template + QueuedRedis& zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + QueuedRedis& zremrangebylex(const StringView &key, const Interval &interval) { + return command(cmd::zremrangebylex, key, interval); + } + + QueuedRedis& zremrangebyrank(const StringView &key, long long start, long long stop) { + return command(cmd::zremrangebyrank, key, start, stop); + } + + template + QueuedRedis& zremrangebyscore(const StringView &key, const Interval &interval) { + return command(cmd::zremrangebyscore, key, interval); + } + + // See comments on *ZRANGE*. + QueuedRedis& zrevrange(const StringView &key, + long long start, + long long stop, + bool with_scores = false) { + return command(cmd::zrevrange, key, start, stop, with_scores); + } + + template + QueuedRedis& zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return command(cmd::zrevrangebylex, key, interval, opts); + } + + template + QueuedRedis& zrevrangebylex(const StringView &key, const Interval &interval) { + return zrevrangebylex(key, interval, {}); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores = false) { + return command(cmd::zrevrangebyscore, key, interval, opts, with_scores); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrevrangebyscore(const StringView &key, + const Interval &interval, + bool with_scores = false) { + return zrevrangebyscore(key, interval, {}, with_scores); + } + + QueuedRedis& zrevrank(const StringView &key, const StringView &member) { + return command(cmd::zrevrank, key, member); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::zscan, key, cursor, pattern, count); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return zscan(key, cursor, pattern, 10); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + long long count) { + return zscan(key, cursor, "*", count); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor) { + return zscan(key, cursor, "*", 10); + } + + QueuedRedis& zscore(const StringView &key, const StringView &member) { + return command(cmd::zscore, key, member); + } + + QueuedRedis& zunionstore(const StringView &destination, + const StringView &key, + double weight) { + return command(cmd::zunionstore, destination, key, weight); + } + + template + QueuedRedis& zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM) { + range_check("ZUNIONSTORE", first, last); + + return command(cmd::zunionstore_range, destination, first, last, type); + } + + template + QueuedRedis& zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + QueuedRedis& pfadd(const StringView &key, const StringView &element) { + return command(cmd::pfadd, key, element); + } + + template + QueuedRedis& pfadd(const StringView &key, Input first, Input last) { + range_check("PFADD", first, last); + + return command(cmd::pfadd_range, key, first, last); + } + + template + QueuedRedis& pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + QueuedRedis& pfcount(const StringView &key) { + return command(cmd::pfcount, key); + } + + template + QueuedRedis& pfcount(Input first, Input last) { + range_check("PFCOUNT", first, last); + + return command(cmd::pfcount_range, first, last); + } + + template + QueuedRedis& pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + QueuedRedis& pfmerge(const StringView &destination, const StringView &key) { + return command(cmd::pfmerge, destination, key); + } + + template + QueuedRedis& pfmerge(const StringView &destination, Input first, Input last) { + range_check("PFMERGE", first, last); + + return command(cmd::pfmerge_range, destination, first, last); + } + + template + QueuedRedis& pfmerge(const StringView &destination, std::initializer_list il) { + return pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + QueuedRedis& geoadd(const StringView &key, + const std::tuple &member) { + return command(cmd::geoadd, key, member); + } + + template + QueuedRedis& geoadd(const StringView &key, + Input first, + Input last) { + range_check("GEOADD", first, last); + + return command(cmd::geoadd_range, key, first, last); + } + + template + QueuedRedis& geoadd(const StringView &key, std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + QueuedRedis& geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M) { + return command(cmd::geodist, key, member1, member2, unit); + } + + // Call reply::parse_leniently to parse the reply. + QueuedRedis& geohash(const StringView &key, const StringView &member) { + return command(cmd::geohash, key, member); + } + + template + QueuedRedis& geohash(const StringView &key, Input first, Input last) { + range_check("GEOHASH", first, last); + + return command(cmd::geohash_range, key, first, last); + } + + template + QueuedRedis& geohash(const StringView &key, std::initializer_list il) { + return geohash(key, il.begin(), il.end()); + } + + // Call reply::parse_leniently to parse the reply. + QueuedRedis& geopos(const StringView &key, const StringView &member) { + return command(cmd::geopos, key, member); + } + + template + QueuedRedis& geopos(const StringView &key, Input first, Input last) { + range_check("GEOPOS", first, last); + + return command(cmd::geopos_range, key, first, last); + } + + template + QueuedRedis& geopos(const StringView &key, std::initializer_list il) { + return geopos(key, il.begin(), il.end()); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + QueuedRedis& georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::georadius_store, + key, + loc, + radius, + unit, + destination, + store_dist, + count); + } + + // NOTE: *QueuedRedis::georadius*'s parameters are different from *Redis::georadius*. + // *Redis::georadius* is overloaded by the output iterator, however, there's no such + // iterator in *QueuedRedis::georadius*. So we have to use extra parameters to decide + // whether we should send options to Redis. This also applies to *GEORADIUSBYMEMBER*. + QueuedRedis& georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + return command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + } + + QueuedRedis& georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::georadiusbymember, + key, + member, + radius, + unit, + destination, + store_dist, + count); + } + + // See the comments on *GEORADIUS*. + QueuedRedis& georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + return command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + } + + // SCRIPTING commands. + + template + QueuedRedis& eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + return command(cmd::eval, script, keys_first, keys_last, args_first, args_last); + } + + QueuedRedis& eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return eval(script, keys.begin(), keys.end(), args.begin(), args.end()); + } + + template + QueuedRedis& evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + return command(cmd::evalsha, script, keys_first, keys_last, args_first, args_last); + } + + QueuedRedis& evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return evalsha(script, keys.begin(), keys.end(), args.begin(), args.end()); + } + + // Call reply::parse_leniently to parse the reply. + QueuedRedis& script_exists(const StringView &sha1) { + return command(cmd::script_exists, sha1); + } + + template + QueuedRedis& script_exists(Input first, Input last) { + range_check("SCRIPT EXISTS", first, last); + + return command(cmd::script_exists_range, first, last); + } + + template + QueuedRedis& script_exists(std::initializer_list il) { + return script_exists(il.begin(), il.end()); + } + + QueuedRedis& script_flush() { + return command(cmd::script_flush); + } + + QueuedRedis& script_kill() { + return command(cmd::script_kill); + } + + QueuedRedis& script_load(const StringView &script) { + return command(cmd::script_load, script); + } + + // PUBSUB commands. + + QueuedRedis& publish(const StringView &channel, const StringView &message) { + return command(cmd::publish, channel, message); + } + + // Stream commands. + + QueuedRedis& xack(const StringView &key, const StringView &group, const StringView &id) { + return command(cmd::xack, key, group, id); + } + + template + QueuedRedis& xack(const StringView &key, const StringView &group, Input first, Input last) { + range_check("XACK", first, last); + + return command(cmd::xack_range, key, group, first, last); + } + + template + QueuedRedis& xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + QueuedRedis& xadd(const StringView &key, const StringView &id, Input first, Input last) { + range_check("XADD", first, last); + + return command(cmd::xadd_range, key, id, first, last); + } + + template + QueuedRedis& xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + QueuedRedis& xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true) { + range_check("XADD", first, last); + + return command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + } + + template + QueuedRedis& xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id) { + return command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + } + + template + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last) { + range_check("XCLAIM", first, last); + + return command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + } + + template + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il) { + return xclaim(key, group, consumer, min_idle_time, il.begin(), il.end()); + } + + QueuedRedis& xdel(const StringView &key, const StringView &id) { + return command(cmd::xdel, key, id); + } + + template + QueuedRedis& xdel(const StringView &key, Input first, Input last) { + range_check("XDEL", first, last); + + return command(cmd::xdel_range, key, first, last); + } + + template + QueuedRedis& xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + QueuedRedis& xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false) { + return command(cmd::xgroup_create, key, group, id, mkstream); + } + + QueuedRedis& xgroup_setid(const StringView &key, + const StringView &group, + const StringView &id) { + return command(cmd::xgroup_setid, key, group, id); + } + + QueuedRedis& xgroup_destroy(const StringView &key, const StringView &group) { + return command(cmd::xgroup_destroy, key, group); + } + + QueuedRedis& xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer) { + return command(cmd::xgroup_delconsumer, key, group, consumer); + } + + QueuedRedis& xlen(const StringView &key) { + return command(cmd::xlen, key); + } + + QueuedRedis& xpending(const StringView &key, const StringView &group) { + return command(cmd::xpending, key, group); + } + + QueuedRedis& xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count) { + return command(cmd::xpending_detail, key, group, start, end, count); + } + + QueuedRedis& xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer) { + return command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + } + + QueuedRedis& xrange(const StringView &key, + const StringView &start, + const StringView &end) { + return command(cmd::xrange, key, start, end); + } + + QueuedRedis& xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count) { + return command(cmd::xrange_count, key, start, end, count); + } + + QueuedRedis& xread(const StringView &key, const StringView &id, long long count) { + return command(cmd::xread, key, id, count); + } + + QueuedRedis& xread(const StringView &key, const StringView &id) { + return xread(key, id, 0); + } + + template + auto xread(Input first, Input last, long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREAD", first, last); + + return command(cmd::xread_range, first, last, count); + } + + template + auto xread(Input first, Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xread(first, last, 0); + } + + QueuedRedis& xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count) { + return command(cmd::xread_block, key, id, timeout.count(), count); + } + + QueuedRedis& xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout) { + return xread(key, id, timeout, 0); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREAD", first, last); + + return command(cmd::xread_block_range, first, last, timeout.count(), count); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xread(first, last, timeout, 0); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack) { + return command(cmd::xreadgroup, group, consumer, key, id, count, noack); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count) { + return xreadgroup(group, consumer, key, id, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREADGROUP", first, last); + + return command(cmd::xreadgroup_range, group, consumer, first, last, count, noack); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first ,last, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first ,last, 0, false); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack) { + return command(cmd::xreadgroup_block, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count) { + return xreadgroup(group, consumer, key, id, timeout, count, false); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout) { + return xreadgroup(group, consumer, key, id, timeout, 0, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREADGROUP", first, last); + + return command(cmd::xreadgroup_block_range, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first, last, timeout, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first, last, timeout, 0, false); + } + + QueuedRedis& xrevrange(const StringView &key, + const StringView &end, + const StringView &start) { + return command(cmd::xrevrange, key, end, start); + } + + QueuedRedis& xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count) { + return command(cmd::xrevrange_count, key, end, start, count); + } + + QueuedRedis& xtrim(const StringView &key, long long count, bool approx = true) { + return command(cmd::xtrim, key, count, approx); + } + +private: + friend class Redis; + + friend class RedisCluster; + + template + QueuedRedis(const ConnectionPoolSPtr &pool, bool new_connection, Args &&...args); + + Connection& _connection(); + + void _sanity_check(); + + void _reset(bool reset_connection = true); + + void _return_connection(); + + void _invalidate(); + + void _clean_up(); + + void _rewrite_replies(std::vector &replies) const; + + template + void _rewrite_replies(const std::vector &indexes, + Func rewriter, + std::vector &replies) const; + + GuardedConnectionSPtr _guarded_connection; + + ConnectionPoolSPtr _connection_pool; + + bool _new_connection = true; + + Impl _impl; + + std::size_t _cmd_num = 0; + + std::vector _set_cmd_indexes; + + std::vector _empty_array_cmd_indexes; + + bool _valid = true; +}; + +class QueuedReplies { +public: + QueuedReplies() = default; + + QueuedReplies(const QueuedReplies &) = delete; + QueuedReplies& operator=(const QueuedReplies &) = delete; + + QueuedReplies(QueuedReplies &&) = default; + QueuedReplies& operator=(QueuedReplies &&) = default; + + ~QueuedReplies() = default; + + std::size_t size() const; + + redisReply& get(std::size_t idx); + + template + Result get(std::size_t idx); + + template + void get(std::size_t idx, Output output); + +private: + template + friend class QueuedRedis; + + explicit QueuedReplies(std::vector replies) : _replies(std::move(replies)) {} + + void _index_check(std::size_t idx) const; + + std::vector _replies; +}; + +} + +} + +#include "queued_redis.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.hpp b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.hpp new file mode 100644 index 000000000..d2865e46e --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.hpp @@ -0,0 +1,275 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP +#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP + +namespace sw { + +namespace redis { + +template +template +QueuedRedis::QueuedRedis(const ConnectionPoolSPtr &pool, + bool new_connection, + Args &&...args) : + _new_connection(new_connection), + _impl(std::forward(args)...) { + assert(pool); + + if (_new_connection) { + _connection_pool = std::make_shared(pool->clone()); + } else { + // Create a connection from the origin pool. + _connection_pool = pool; + } +} + +template +QueuedRedis::~QueuedRedis() { + try { + _clean_up(); + } catch (const Error &e) { + // Ensure the destructor does not throw + } +} + +template +Redis QueuedRedis::redis() { + _sanity_check(); + + assert(_guarded_connection); + + return Redis(_guarded_connection); +} + +template +template +auto QueuedRedis::command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, + QueuedRedis&>::type { + try { + _sanity_check(); + + _impl.command(_connection(), cmd, std::forward(args)...); + + ++_cmd_num; + } catch (const Error &e) { + _invalidate(); + throw; + } + + return *this; +} + +template +template +QueuedRedis& QueuedRedis::command(const StringView &cmd_name, Args &&...args) { + auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, std::forward(args)...); + connection.send(cmd_args); + }; + + return command(cmd, cmd_name, std::forward(args)...); +} + +template +template +auto QueuedRedis::command(Input first, Input last) + -> typename std::enable_if::value, QueuedRedis&>::type { + if (first == last) { + throw Error("command: empty range"); + } + + auto cmd = [](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +QueuedReplies QueuedRedis::exec() { + try { + _sanity_check(); + + auto replies = _impl.exec(_connection(), _cmd_num); + + _rewrite_replies(replies); + + _reset(); + + return QueuedReplies(std::move(replies)); + } catch (const WatchError &e) { + // In this case, we only clear some states and keep the connection, + // so that user can retry the transaction. + _reset(false); + throw; + } catch (const Error &e) { + _invalidate(); + throw; + } +} + +template +void QueuedRedis::discard() { + try { + _sanity_check(); + + _impl.discard(_connection(), _cmd_num); + + _reset(); + } catch (const Error &e) { + _invalidate(); + throw; + } +} + +template +Connection& QueuedRedis::_connection() { + assert(_valid); + + if (!_guarded_connection) { + _guarded_connection = std::make_shared(_connection_pool); + } + + return _guarded_connection->connection(); +} + +template +void QueuedRedis::_sanity_check() { + if (!_valid) { + throw Error("Not in valid state"); + } + + if (_connection().broken()) { + throw Error("Connection is broken"); + } +} + +template +inline void QueuedRedis::_reset(bool reset_connection) { + if (reset_connection && !_new_connection) { + _return_connection(); + } + + _cmd_num = 0; + + _set_cmd_indexes.clear(); + + _empty_array_cmd_indexes.clear(); +} + +template +inline void QueuedRedis::_return_connection() { + if (_guarded_connection.use_count() == 1) { + // If no one else holding the connection, return it back to pool. + // Instead, if some other `Redis` object holds the connection, + // e.g. `auto redis = transaction.redis();`, we cannot return the connection. + _guarded_connection.reset(); + } +} + +template +void QueuedRedis::_invalidate() { + _valid = false; + + _clean_up(); + + _reset(); +} + +template +void QueuedRedis::_clean_up() { + if (_guarded_connection && !_new_connection) { + // Something bad happened, we need to close the current connection + // before returning it back to pool. + _guarded_connection->connection().invalidate(); + } +} + +template +void QueuedRedis::_rewrite_replies(std::vector &replies) const { + _rewrite_replies(_set_cmd_indexes, reply::rewrite_set_reply, replies); + + _rewrite_replies(_empty_array_cmd_indexes, reply::rewrite_empty_array_reply, replies); +} + +template +template +void QueuedRedis::_rewrite_replies(const std::vector &indexes, + Func rewriter, + std::vector &replies) const { + for (auto idx : indexes) { + assert(idx < replies.size()); + + auto &reply = replies[idx]; + + assert(reply); + + rewriter(*reply); + } +} + +inline std::size_t QueuedReplies::size() const { + return _replies.size(); +} + +inline redisReply& QueuedReplies::get(std::size_t idx) { + _index_check(idx); + + auto &reply = _replies[idx]; + + assert(reply); + + if (reply::is_error(*reply)) { + throw_error(*reply); + } + + return *reply; +} + +template +inline Result QueuedReplies::get(std::size_t idx) { + auto &reply = get(idx); + + return reply::parse(reply); +} + +template +inline void QueuedReplies::get(std::size_t idx, Output output) { + auto &reply = get(idx); + + reply::to_array(reply, output); +} + +inline void QueuedReplies::_index_check(std::size_t idx) const { + if (idx >= size()) { + throw Error("Out of range"); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis++.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis++.h new file mode 100644 index 000000000..0da0ebb16 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis++.h @@ -0,0 +1,25 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H +#define SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H + +#include "redis.h" +#include "redis_cluster.h" +#include "queued_redis.h" +#include "sentinel.h" + +#endif // end SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.h new file mode 100644 index 000000000..1333e6b68 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.h @@ -0,0 +1,3631 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_H +#define SEWENEW_REDISPLUSPLUS_REDIS_H + +#include +#include +#include +#include +#include +#include "connection_pool.h" +#include "reply.h" +#include "command_options.h" +#include "utils.h" +#include "subscriber.h" +#include "pipeline.h" +#include "transaction.h" +#include "sentinel.h" + +namespace sw { + +namespace redis { + +template +class QueuedRedis; + +using Transaction = QueuedRedis; + +using Pipeline = QueuedRedis; + +class Redis { +public: + /// @brief Construct `Redis` instance with connection options and connection pool options. + /// @param connection_opts Connection options. + /// @param pool_opts Connection pool options. + /// @see `ConnectionOptions` + /// @see `ConnectionPoolOptions` + /// @see https://github.com/sewenew/redis-plus-plus#connection + explicit Redis(const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : + _pool(std::make_shared(pool_opts, connection_opts)) {} + + /// @brief Construct `Redis` instance with URI. + /// @param uri URI, e.g. 'tcp://127.0.0.1', 'tcp://127.0.0.1:6379', or 'unix://path/to/socket'. + /// Full URI scheme: 'tcp://[[username:]password@]host[:port][/db]' or + /// unix://[[username:]password@]path-to-unix-domain-socket[/db] + /// @see https://github.com/sewenew/redis-plus-plus/issues/37 + /// @see https://github.com/sewenew/redis-plus-plus#connection + explicit Redis(const std::string &uri); + + /// @brief Construct `Redis` instance with Redis sentinel, i.e. get node info from sentinel. + /// @param sentinel `Sentinel` instance. + /// @param master_name Name of master node. + /// @param role Connect to master node or slave node. + /// - Role::MASTER: Connect to master node. + /// - Role::SLAVE: Connect to slave node. + /// @param connection_opts Connection options. + /// @param pool_opts Connection pool options. + /// @see `Sentinel` + /// @see `Role` + /// @see https://github.com/sewenew/redis-plus-plus#redis-sentinel + Redis(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role, + const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : + _pool(std::make_shared(SimpleSentinel(sentinel, master_name, role), + pool_opts, + connection_opts)) {} + + /// @brief `Redis` is not copyable. + Redis(const Redis &) = delete; + + /// @brief `Redis` is not copyable. + Redis& operator=(const Redis &) = delete; + + /// @brief `Redis` is movable. + Redis(Redis &&) = default; + + /// @brief `Redis` is movable. + Redis& operator=(Redis &&) = default; + + /// @brief Create a pipeline. + /// @param new_connection Whether creating a `Pipeline` object in a new connection. + /// @return The created pipeline. + /// @note Instead of picking a connection from the underlying connection pool, + /// this method will create a new connection to Redis. So it's not a cheap operation, + /// and you'd better reuse the returned object as much as possible. + /// @see https://github.com/sewenew/redis-plus-plus#pipeline + Pipeline pipeline(bool new_connection = true); + + /// @brief Create a transaction. + /// @param piped Whether commands in a transaction should be sent in a pipeline to reduce RTT. + /// @param new_connection Whether creating a `Pipeline` object in a new connection. + /// @return The created transaction. + /// @note Instead of picking a connection from the underlying connection pool, + /// this method will create a new connection to Redis. So it's not a cheap operation, + /// and you'd better reuse the returned object as much as possible. + /// @see https://github.com/sewenew/redis-plus-plus#transaction + Transaction transaction(bool piped = false, bool new_connection = true); + + /// @brief Create a subscriber. + /// @return The created subscriber. + /// @note Instead of picking a connection from the underlying connection pool, + /// this method will create a new connection to Redis. So it's not a cheap operation, + /// and you'd better reuse the returned object as much as possible. + /// @see https://github.com/sewenew/redis-plus-plus#publishsubscribe + Subscriber subscriber(); + + template + auto command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, void>::type; + + template + Result command(const StringView &cmd_name, Args &&...args); + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Result>::type; + + template + auto command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type; + + // CONNECTION commands. + + /// @brief Send password to Redis. + /// @param password Password. + /// @note Normally, you should not call this method. + /// Instead, you should set password with `ConnectionOptions` or URI. + /// @see https://redis.io/commands/auth + void auth(const StringView &password); + + /// @brief Send user and password to Redis. + /// @param user User name. + /// @param password Password. + /// @note Normally, you should not call this method. + /// Instead, you should set password with `ConnectionOptions` or URI. + /// Also this overload only works with Redis 6.0 or later. + /// @see https://redis.io/commands/auth + void auth(const StringView &user, const StringView &password); + + /// @brief Ask Redis to return the given message. + /// @param msg Message to be sent. + /// @return Return the given message. + /// @see https://redis.io/commands/echo + std::string echo(const StringView &msg); + + /// @brief Test if the connection is alive. + /// @return Always return *PONG*. + /// @see https://redis.io/commands/ping + std::string ping(); + + /// @brief Test if the connection is alive. + /// @param msg Message sent to Redis. + /// @return Return the given message. + /// @see https://redis.io/commands/ping + std::string ping(const StringView &msg); + + // After sending QUIT, only the current connection will be close, while + // other connections in the pool is still open. This is a strange behavior. + // So we DO NOT support the QUIT command. If you want to quit connection to + // server, just destroy the Redis object. + // + // void quit(); + + // We get a connection from the pool, and send the SELECT command to switch + // to a specified DB. However, when we try to send other commands to the + // given DB, we might get a different connection from the pool, and these + // commands, in fact, work on other DB. e.g. + // + // redis.select(1); // get a connection from the pool and switch to the 1th DB + // redis.get("key"); // might get another connection from the pool, + // // and try to get 'key' on the default DB + // + // Obviously, this is NOT what we expect. So we DO NOT support SELECT command. + // In order to select a DB, we can specify the DB index with the ConnectionOptions. + // + // However, since Pipeline and Transaction always send multiple commands on a + // single connection, these two classes have a *select* method. + // + // void select(long long idx); + + /// @brief Swap two Redis databases. + /// @param idx1 The index of the first database. + /// @param idx2 The index of the second database. + /// @see https://redis.io/commands/swapdb + void swapdb(long long idx1, long long idx2); + + // SERVER commands. + + /// @brief Rewrite AOF in the background. + /// @see https://redis.io/commands/bgrewriteaof + void bgrewriteaof(); + + /// @brief Save database in the background. + /// @see https://redis.io/commands/bgsave + void bgsave(); + + /// @brief Get the size of the currently selected database. + /// @return Number of keys in currently selected database. + /// @see https://redis.io/commands/dbsize + long long dbsize(); + + /// @brief Remove keys of all databases. + /// @param async Whether flushing databases asynchronously, i.e. without blocking the server. + /// @see https://redis.io/commands/flushall + void flushall(bool async = false); + + /// @brief Remove keys of current databases. + /// @param async Whether flushing databases asynchronously, i.e. without blocking the server. + /// @see https://redis.io/commands/flushdb + void flushdb(bool async = false); + + /// @brief Get the info about the server. + /// @return Server info. + /// @see https://redis.io/commands/info + std::string info(); + + /// @brief Get the info about the server on the given section. + /// @param section Section. + /// @return Server info. + /// @see https://redis.io/commands/info + std::string info(const StringView §ion); + + /// @brief Get the UNIX timestamp in seconds, at which the database was saved successfully. + /// @return The last saving time. + /// @see https://redis.io/commands/lastsave + long long lastsave(); + + /// @brief Save databases into RDB file **synchronously**, i.e. block the server during saving. + /// @see https://redis.io/commands/save + void save(); + + // KEY commands. + + /// @brief Delete the given key. + /// @param key Key. + /// @return Number of keys removed. + /// @retval 1 If the key exists, and has been removed. + /// @retval 0 If the key does not exist. + /// @see https://redis.io/commands/del + long long del(const StringView &key); + + /// @brief Delete the given list of keys. + /// @param first Iterator to the first key in the range. + /// @param last Off-the-end iterator to the given range. + /// @return Number of keys removed. + /// @see https://redis.io/commands/del + template + long long del(Input first, Input last); + + /// @brief Delete the given list of keys. + /// @param il Initializer list of keys to be deleted. + /// @return Number of keys removed. + /// @see https://redis.io/commands/del + template + long long del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + /// @brief Get the serialized valued stored at key. + /// @param key Key. + /// @return The serialized value. + /// @note If key does not exist, `dump` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/dump + OptionalString dump(const StringView &key); + + /// @brief Check if the given key exists. + /// @param key Key. + /// @return Whether the given key exists. + /// @retval 1 If key exists. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/exists + long long exists(const StringView &key); + + /// @brief Check if the given keys exist. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the given range. + /// @return Number of keys existing. + /// @see https://redis.io/commands/exists + template + long long exists(Input first, Input last); + + /// @brief Check if the given keys exist. + /// @param il Initializer list of keys to be checked. + /// @return Number of keys existing. + /// @see https://redis.io/commands/exists + template + long long exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in seconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expire + bool expire(const StringView &key, long long timeout); + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in seconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expire + bool expire(const StringView &key, const std::chrono::seconds &timeout); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in seconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expireat + bool expireat(const StringView &key, long long timestamp); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in seconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expireat + bool expireat(const StringView &key, + const std::chrono::time_point &tp); + + /// @brief Get keys matching the given pattern. + /// @param pattern Pattern. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @note It's always a bad idea to call `keys`, since it might block Redis for a long time, + /// especially when the data set is very big. + /// @see `Redis::scan` + /// @see https://redis.io/commands/keys + template + void keys(const StringView &pattern, Output output); + + /// @brief Move a key to the given database. + /// @param key Key. + /// @param db The destination database. + /// @return Whether key has been moved. + /// @retval true If key has been moved. + /// @retval false If key was not moved. + /// @see https://redis.io/commands/move + bool move(const StringView &key, long long db); + + /// @brief Remove timeout on key. + /// @param key Key. + /// @return Whether timeout has been removed. + /// @retval true If timeout has been removed. + /// @retval false If key does not exist, or does not have an associated timeout. + /// @see https://redis.io/commands/persist + bool persist(const StringView &key); + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in milliseconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpire + bool pexpire(const StringView &key, long long timeout); + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in milliseconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpire + bool pexpire(const StringView &key, const std::chrono::milliseconds &timeout); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in milliseconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpireat + bool pexpireat(const StringView &key, long long timestamp); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in milliseconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpireat + bool pexpireat(const StringView &key, + const std::chrono::time_point &tp); + + /// @brief Get the TTL of a key in milliseconds. + /// @param key Key. + /// @return TTL of the key in milliseconds. + /// @see https://redis.io/commands/pttl + long long pttl(const StringView &key); + + /// @brief Get a random key from current database. + /// @return A random key. + /// @note If the database is empty, `randomkey` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/randomkey + OptionalString randomkey(); + + /// @brief Rename `key` to `newkey`. + /// @param key Key to be renamed. + /// @param newkey The new name of the key. + /// @see https://redis.io/commands/rename + void rename(const StringView &key, const StringView &newkey); + + /// @brief Rename `key` to `newkey` if `newkey` does not exist. + /// @param key Key to be renamed. + /// @param newkey The new name of the key. + /// @return Whether key has been renamed. + /// @retval true If key has been renamed. + /// @retval false If newkey already exists. + /// @see https://redis.io/commands/renamenx + bool renamenx(const StringView &key, const StringView &newkey); + + /// @brief Create a key with the value obtained by `Redis::dump`. + /// @param key Key. + /// @param val Value obtained by `Redis::dump`. + /// @param ttl Timeout of the created key in milliseconds. If `ttl` is 0, set no timeout. + /// @param replace Whether to overwrite an existing key. + /// If `replace` is `true` and key already exists, throw an exception. + /// @see https://redis.io/commands/restore + void restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false); + + /// @brief Create a key with the value obtained by `Redis::dump`. + /// @param key Key. + /// @param val Value obtained by `Redis::dump`. + /// @param ttl Timeout of the created key in milliseconds. If `ttl` is 0, set no timeout. + /// @param replace Whether to overwrite an existing key. + /// If `replace` is `true` and key already exists, throw an exception. + /// @see https://redis.io/commands/restore + void restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false); + + // TODO: sort + + /// @brief Scan keys of the database matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::unordered_set keys; + /// while (true) { + /// cursor = redis.scan(cursor, "pattern:*", 10, std::inserter(keys, keys.begin())); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param cursor Cursor. + /// @param pattern Pattern of the keys to be scanned. + /// @param count A hint for how many keys to be scanned. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + /// TODO: support the TYPE option for Redis 6.0. + template + long long scan(long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan all keys of the database. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + template + long long scan(long long cursor, + Output output); + + /// @brief Scan keys of the database matching the given pattern. + /// @param cursor Cursor. + /// @param pattern Pattern of the keys to be scanned. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + template + long long scan(long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan keys of the database matching the given pattern. + /// @param cursor Cursor. + /// @param count A hint for how many keys to be scanned. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + template + long long scan(long long cursor, + long long count, + Output output); + + /// @brief Update the last access time of the given key. + /// @param key Key. + /// @return Whether last access time of the key has been updated. + /// @retval 1 If key exists, and last access time has been updated. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/touch + long long touch(const StringView &key); + + /// @brief Update the last access time of the given key. + /// @param first Iterator to the first key to be touched. + /// @param last Off-the-end iterator to the given range. + /// @return Whether last access time of the key has been updated. + /// @retval 1 If key exists, and last access time has been updated. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/touch + template + long long touch(Input first, Input last); + + /// @brief Update the last access time of the given key. + /// @param il Initializer list of keys to be touched. + /// @return Whether last access time of the key has been updated. + /// @retval 1 If key exists, and last access time has been updated. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/touch + template + long long touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + /// @brief Get the remaining Time-To-Live of a key. + /// @param key Key. + /// @return TTL in seconds. + /// @retval TTL If the key has a timeout. + /// @retval -1 If the key exists but does not have a timeout. + /// @retval -2 If the key does not exist. + /// @note In Redis 2.6 or older, `ttl` returns -1 if the key does not exist, + /// or if the key exists but does not have a timeout. + /// @see https://redis.io/commands/ttl + long long ttl(const StringView &key); + + /// @brief Get the type of the value stored at key. + /// @param key Key. + /// @return The type of the value. + /// @see https://redis.io/commands/type + std::string type(const StringView &key); + + /// @brief Remove the given key asynchronously, i.e. without blocking Redis. + /// @param key Key. + /// @return Whether the key has been removed. + /// @retval 1 If key exists, and has been removed. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/unlink + long long unlink(const StringView &key); + + /// @brief Remove the given keys asynchronously, i.e. without blocking Redis. + /// @param first Iterator to the first key in the given range. + /// @param last Off-the-end iterator to the given range. + /// @return Number of keys that have been removed. + /// @see https://redis.io/commands/unlink + template + long long unlink(Input first, Input last); + + /// @brief Remove the given keys asynchronously, i.e. without blocking Redis. + /// @param il Initializer list of keys to be unlinked. + /// @return Number of keys that have been removed. + /// @see https://redis.io/commands/unlink + template + long long unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + /// @brief Wait until previous write commands are successfully replicated to at + /// least the specified number of replicas or the given timeout has been reached. + /// @param numslaves Number of replicas. + /// @param timeout Timeout in milliseconds. If timeout is 0ms, wait forever. + /// @return Number of replicas that have been successfully replicated these write commands. + /// @note The return value might be less than `numslaves`, because timeout has been reached. + /// @see https://redis.io/commands/wait + long long wait(long long numslaves, long long timeout); + + /// @brief Wait until previous write commands are successfully replicated to at + /// least the specified number of replicas or the given timeout has been reached. + /// @param numslaves Number of replicas. + /// @param timeout Timeout in milliseconds. If timeout is 0ms, wait forever. + /// @return Number of replicas that have been successfully replicated these write commands. + /// @note The return value might be less than `numslaves`, because timeout has been reached. + /// @see https://redis.io/commands/wait + /// TODO: Support default parameter for `timeout` to wait forever. + long long wait(long long numslaves, const std::chrono::milliseconds &timeout); + + // STRING commands. + + /// @brief Append the given string to the string stored at key. + /// @param key Key. + /// @param str String to be appended. + /// @return The length of the string after the append operation. + /// @see https://redis.io/commands/append + long long append(const StringView &key, const StringView &str); + + /// @brief Get the number of bits that have been set for the given range of the string. + /// @param key Key. + /// @param start Start index (inclusive) of the range. 0 means the beginning of the string. + /// @param end End index (inclusive) of the range. -1 means the end of the string. + /// @return Number of bits that have been set. + /// @note The index can be negative to index from the end of the string. + /// @see https://redis.io/commands/bitcount + long long bitcount(const StringView &key, long long start = 0, long long end = -1); + + /// @brief Do bit operation on the string stored at `key`, and save the result to `destination`. + /// @param op Bit operations. + /// @param destination The destination key where the result is saved. + /// @param key The key where the string to be operated is stored. + /// @return The length of the string saved at `destination`. + /// @see https://redis.io/commands/bitop + /// @see `BitOp` + long long bitop(BitOp op, const StringView &destination, const StringView &key); + + /// @brief Do bit operations on the strings stored at the given keys, + /// and save the result to `destination`. + /// @param op Bit operations. + /// @param destination The destination key where the result is saved. + /// @param first Iterator to the first key where the string to be operated is stored. + /// @param last Off-the-end iterator to the given range of keys. + /// @return The length of the string saved at `destination`. + /// @see https://redis.io/commands/bitop + /// @see `BitOp` + template + long long bitop(BitOp op, const StringView &destination, Input first, Input last); + + /// @brief Do bit operations on the strings stored at the given keys, + /// and save the result to `destination`. + /// @param op Bit operations. + /// @param destination The destination key where the result is saved. + /// @param il Initializer list of keys where the strings are operated. + /// @return The length of the string saved at `destination`. + /// @see https://redis.io/commands/bitop + /// @see `BitOp` + template + long long bitop(BitOp op, const StringView &destination, std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + /// @brief Get the position of the first bit set to 0 or 1 in the given range of the string. + /// @param key Key. + /// @param bit 0 or 1. + /// @param start Start index (inclusive) of the range. 0 means the beginning of the string. + /// @param end End index (inclusive) of the range. -1 means the end of the string. + /// @return The position of the first bit set to 0 or 1. + /// @see https://redis.io/commands/bitpos + long long bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1); + + /// @brief Decrement the integer stored at key by 1. + /// @param key Key. + /// @return The value after the decrement. + /// @see https://redis.io/commands/decr + long long decr(const StringView &key); + + /// @brief Decrement the integer stored at key by `decrement`. + /// @param key Key. + /// @param decrement Decrement. + /// @return The value after the decrement. + /// @see https://redis.io/commands/decrby + long long decrby(const StringView &key, long long decrement); + + /// @brief Get the string value stored at key. + /// + /// Example: + /// @code{.cpp} + /// auto val = redis.get("key"); + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "key not exist" << std::endl; + /// @endcode + /// @param key Key. + /// @return The value stored at key. + /// @note If key does not exist, `get` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/get + OptionalString get(const StringView &key); + + /// @brief Get the bit value at offset in the string. + /// @param key Key. + /// @param offset Offset. + /// @return The bit value. + /// @see https://redis.io/commands/getbit + long long getbit(const StringView &key, long long offset); + + /// @brief Get the substring of the string stored at key. + /// @param key Key. + /// @param start Start index (inclusive) of the range. 0 means the beginning of the string. + /// @param end End index (inclusive) of the range. -1 means the end of the string. + /// @return The substring in range [start, end]. If key does not exist, return an empty string. + /// @see https://redis.io/commands/getrange + std::string getrange(const StringView &key, long long start, long long end); + + /// @brief Atomically set the string stored at `key` to `val`, and return the old value. + /// @param key Key. + /// @param val Value to be set. + /// @return The old value stored at key. + /// @note If key does not exist, `getset` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/getset + /// @see `OptionalString` + OptionalString getset(const StringView &key, const StringView &val); + + /// @brief Increment the integer stored at key by 1. + /// @param key Key. + /// @return The value after the increment. + /// @see https://redis.io/commands/incr + long long incr(const StringView &key); + + /// @brief Increment the integer stored at key by `increment`. + /// @param key Key. + /// @param increment Increment. + /// @return The value after the increment. + /// @see https://redis.io/commands/incrby + long long incrby(const StringView &key, long long increment); + + /// @brief Increment the floating point number stored at key by `increment`. + /// @param key Key. + /// @param increment Increment. + /// @return The value after the increment. + /// @see https://redis.io/commands/incrbyfloat + double incrbyfloat(const StringView &key, double increment); + + /// @brief Get the values of multiple keys atomically. + /// + /// Example: + /// @code{.cpp} + /// std::vector keys = {"k1", "k2", "k3"}; + /// std::vector vals; + /// redis.mget(keys.begin(), keys.end(), std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "key does not exist" << std::endl; + /// } + /// @endcode + /// @param first Iterator to the first key of the given range. + /// @param last Off-the-end iterator to the given range. + /// @param output Output iterator to the destination where the values are stored. + /// @note The destination should be a container of `OptionalString` type, + /// since the given key might not exist (in this case, the value of the corresponding + /// key is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/mget + template + void mget(Input first, Input last, Output output); + + /// @brief Get the values of multiple keys atomically. + /// + /// Example: + /// @code{.cpp} + /// std::vector vals; + /// redis.mget({"k1", "k2", "k3"}, std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "key does not exist" << std::endl; + /// } + /// @endcode + /// @param il Initializer list of keys. + /// @param output Output iterator to the destination where the values are stored. + /// @note The destination should be a container of `OptionalString` type, + /// since the given key might not exist (in this case, the value of the corresponding + /// key is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/mget + template + void mget(std::initializer_list il, Output output) { + mget(il.begin(), il.end(), output); + } + + /// @brief Set multiple key-value pairs. + /// + /// Example: + /// @code{.cpp} + /// std::vector> kvs1 = {{"k1", "v1"}, {"k2", "v2"}}; + /// redis.mset(kvs1.begin(), kvs1.end()); + /// std::unordered_map kvs2 = {{"k3", "v3"}, {"k4", "v4"}}; + /// redis.mset(kvs2.begin(), kvs2.end()); + /// @endcode + /// @param first Iterator to the first key-value pair. + /// @param last Off-the-end iterator to the given range. + /// @see https://redis.io/commands/mset + template + void mset(Input first, Input last); + + /// @brief Set multiple key-value pairs. + /// + /// Example: + /// @code{.cpp} + /// redis.mset({std::make_pair("k1", "v1"), std::make_pair("k2", "v2")}); + /// @endcode + /// @param il Initializer list of key-value pairs. + /// @see https://redis.io/commands/mset + template + void mset(std::initializer_list il) { + mset(il.begin(), il.end()); + } + + /// @brief Set the given key-value pairs if all specified keys do not exist. + /// + /// Example: + /// @code{.cpp} + /// std::vector> kvs1; + /// redis.msetnx(kvs1.begin(), kvs1.end()); + /// std::unordered_map kvs2; + /// redis.msetnx(kvs2.begin(), kvs2.end()); + /// @endcode + /// @param first Iterator to the first key-value pair. + /// @param last Off-the-end iterator of the given range. + /// @return Whether all keys have been set. + /// @retval true If all keys have been set. + /// @retval false If no key was set, i.e. at least one key already exist. + /// @see https://redis.io/commands/msetnx + template + bool msetnx(Input first, Input last); + + /// @brief Set the given key-value pairs if all specified keys do not exist. + /// + /// Example: + /// @code{.cpp} + /// redis.msetnx({make_pair("k1", "v1"), make_pair("k2", "v2")}); + /// @endcode + /// @param il Initializer list of key-value pairs. + /// @return Whether all keys have been set. + /// @retval true If all keys have been set. + /// @retval false If no key was set, i.e. at least one key already exist. + /// @see https://redis.io/commands/msetnx + template + bool msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + /// @brief Set key-value pair with the given timeout in milliseconds. + /// @param key Key. + /// @param ttl Time-To-Live in milliseconds. + /// @param val Value. + /// @see https://redis.io/commands/psetex + void psetex(const StringView &key, + long long ttl, + const StringView &val); + + /// @brief Set key-value pair with the given timeout in milliseconds. + /// @param key Key. + /// @param ttl Time-To-Live in milliseconds. + /// @param val Value. + /// @see https://redis.io/commands/psetex + void psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val); + + /// @brief Set a key-value pair. + /// + /// Example: + /// @code{.cpp} + /// // Set a key-value pair. + /// redis.set("key", "value"); + /// // Set a key-value pair, and expire it after 10 seconds. + /// redis.set("key", "value", std::chrono::seconds(10)); + /// // Set a key-value pair with a timeout, only if the key already exists. + /// if (redis.set("key", "value", std::chrono::seconds(10), UpdateType::EXIST)) + /// std::cout << "OK" << std::endl; + /// else + /// std::cout << "key does not exist" << std::endl; + /// @endcode + /// @param key Key. + /// @param val Value. + /// @param ttl Timeout on the key. If `ttl` is 0ms, do not set timeout. + /// @param type Options for set command: + /// - UpdateType::EXIST: Set the key only if it already exists. + /// - UpdateType::NOT_EXIST: Set the key only if it does not exist. + /// - UpdateType::ALWAYS: Always set the key no matter whether it exists. + /// @return Whether the key has been set. + /// @retval true If the key has been set. + /// @retval false If the key was not set, because of the given option. + /// @see https://redis.io/commands/set + // TODO: Support KEEPTTL option for Redis 6.0 + bool set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS); + + // TODO: add SETBIT command. + + /// @brief Set key-value pair with the given timeout in seconds. + /// @param key Key. + /// @param ttl Time-To-Live in seconds. + /// @param val Value. + /// @see https://redis.io/commands/setex + void setex(const StringView &key, + long long ttl, + const StringView &val); + + /// @brief Set key-value pair with the given timeout in seconds. + /// @param key Key. + /// @param ttl Time-To-Live in seconds. + /// @param val Value. + /// @see https://redis.io/commands/setex + void setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val); + + /// @brief Set the key if it does not exist. + /// @param key Key. + /// @param val Value. + /// @return Whether the key has been set. + /// @retval true If the key has been set. + /// @retval false If the key was not set, i.e. the key already exists. + /// @see https://redis.io/commands/setnx + bool setnx(const StringView &key, const StringView &val); + + /// @brief Set the substring starting from `offset` to the given value. + /// @param key Key. + /// @param offset Offset. + /// @param val Value. + /// @return The length of the string after this operation. + /// @see https://redis.io/commands/setrange + long long setrange(const StringView &key, long long offset, const StringView &val); + + /// @brief Get the length of the string stored at key. + /// @param key Key. + /// @return The length of the string. + /// @note If key does not exist, `strlen` returns 0. + /// @see https://redis.io/commands/strlen + long long strlen(const StringView &key); + + // LIST commands. + + /// @brief Pop the first element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + OptionalStringPair blpop(const StringView &key, long long timeout); + + /// @brief Pop the first element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + OptionalStringPair blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(Input first, Input last, long long timeout); + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param il Initializer list of keys. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param il Initializer list of keys. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop the last element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + OptionalStringPair brpop(const StringView &key, long long timeout); + + /// @brief Pop the last element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + OptionalStringPair brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(Input first, Input last, long long timeout); + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param il Initializer list of lists. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param first Iterator to the first list. + /// @param last Off-the-end iterator to the list range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param il Initializer list of list keys. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop last element of one list and push it to the left of another list in blocking way. + /// @param source Key of the source list. + /// @param destination Key of the destination list. + /// @param timeout Timeout. 0 means block forever. + /// @return The popped element. + /// @note If the source list does not exist, `brpoplpush` returns `OptionalString{}` (`std::nullopt`). + /// @see `Redis::rpoplpush` + /// @see https://redis.io/commands/brpoplpush + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + long long timeout); + + /// @brief Pop last element of one list and push it to the left of another list in blocking way. + /// @param source Key of the source list. + /// @param destination Key of the destination list. + /// @param timeout Timeout. 0 means block forever. + /// @return The popped element. + /// @note If the source list does not exist, `brpoplpush` returns `OptionalString{}` (`std::nullopt`). + /// @see `Redis::rpoplpush` + /// @see https://redis.io/commands/brpoplpush + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Get the element at the given index of the list. + /// @param key Key where the list is stored. + /// @param index Zero-base index, and -1 means the last element. + /// @return The element at the given index. + /// @see https://redis.io/commands/lindex + OptionalString lindex(const StringView &key, long long index); + + /// @brief Insert an element to a list before or after the pivot element. + /// + /// Example: + /// @code{.cpp} + /// // Insert 'hello' before 'world' + /// auto len = redis.linsert("list", InsertPosition::BEFORE, "world", "hello"); + /// if (len == -1) + /// std::cout << "there's no 'world' in the list" << std::endl; + /// else + /// std::cout << "after the operation, the length of the list is " << len << std::endl; + /// @endcode + /// @param key Key where the list is stored. + /// @param position Before or after the pivot element. + /// @param pivot The pivot value. The `pivot` is the value of the element, not the index. + /// @param val Element to be inserted. + /// @return The length of the list after the operation. + /// @note If the pivot value is not found, `linsert` returns -1. + /// @see `InsertPosition` + /// @see https://redis.io/commands/linsert + long long linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + + /// @brief Get the length of the list. + /// @param key Key where the list is stored. + /// @return The length of the list. + /// @see https://redis.io/commands/llen + long long llen(const StringView &key); + + /// @brief Pop the first element of the list. + /// + /// Example: + /// @code{.cpp} + /// auto element = redis.lpop("list"); + /// if (element) + /// std::cout << *element << std::endl; + /// else + /// std::cout << "list is empty, i.e. list does not exist" << std::endl; + /// @endcode + /// @param key Key where the list is stored. + /// @return The popped element. + /// @note If list is empty, i.e. key does not exist, return `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/lpop + OptionalString lpop(const StringView &key); + + /// @brief Push an element to the beginning of the list. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpush + long long lpush(const StringView &key, const StringView &val); + + /// @brief Push multiple elements to the beginning of the list. + /// + /// Example: + /// @code{.cpp} + /// std::vector elements = {"e1", "e2", "e3"}; + /// redis.lpush("list", elements.begin(), elements.end()); + /// @endcode + /// @param key Key where the list is stored. + /// @param first Iterator to the first element to be pushed. + /// @param last Off-the-end iterator to the given element range. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpush + template + long long lpush(const StringView &key, Input first, Input last); + + /// @brief Push multiple elements to the beginning of the list. + /// + /// Example: + /// @code{.cpp} + /// redis.lpush("list", {"e1", "e2", "e3"}); + /// @endcode + /// @param key Key where the list is stored. + /// @param il Initializer list of elements. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpush + template + long long lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + /// @brief Push an element to the beginning of the list, only if the list already exists. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpushx + // TODO: add a multiple elements overload. + long long lpushx(const StringView &key, const StringView &val); + + /// @brief Get elements in the given range of the given list. + /// + /// Example: + /// @code{.cpp} + /// std::vector elements; + /// // Save all elements of a Redis list to a vector of string. + /// redis.lrange("list", 0, -1, std::back_inserter(elements)); + /// @endcode + /// @param key Key where the list is stored. + /// @param start Start index of the range. Index can be negative, which mean index from the end. + /// @param stop End index of the range. + /// @param output Output iterator to the destination where the results are saved. + /// @see https://redis.io/commands/lrange + template + void lrange(const StringView &key, long long start, long long stop, Output output); + + /// @brief Remove the first `count` occurrences of elements equal to `val`. + /// @param key Key where the list is stored. + /// @param count Number of occurrences to be removed. + /// @param val Value. + /// @return Number of elements removed. + /// @note `count` can be positive, negative and 0. Check the reference for detail. + /// @see https://redis.io/commands/lrem + long long lrem(const StringView &key, long long count, const StringView &val); + + /// @brief Set the element at the given index to the specified value. + /// @param key Key where the list is stored. + /// @param index Index of the element to be set. + /// @param val Value. + /// @see https://redis.io/commands/lset + void lset(const StringView &key, long long index, const StringView &val); + + /// @brief Trim a list to keep only element in the given range. + /// @param key Key where the key is stored. + /// @param start Start of the index. + /// @param stop End of the index. + /// @see https://redis.io/commands/ltrim + void ltrim(const StringView &key, long long start, long long stop); + + /// @brief Pop the last element of a list. + /// @param key Key where the list is stored. + /// @return The popped element. + /// @note If the list is empty, i.e. key does not exist, `rpop` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/rpop + OptionalString rpop(const StringView &key); + + /// @brief Pop last element of one list and push it to the left of another list. + /// @param source Key of the source list. + /// @param destination Key of the destination list. + /// @return The popped element. + /// @note If the source list does not exist, `rpoplpush` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/brpoplpush + OptionalString rpoplpush(const StringView &source, const StringView &destination); + + /// @brief Push an element to the end of the list. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpush + long long rpush(const StringView &key, const StringView &val); + + /// @brief Push multiple elements to the end of the list. + /// @param key Key where the list is stored. + /// @param first Iterator to the first element to be pushed. + /// @param last Off-the-end iterator to the given element range. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpush + template + long long rpush(const StringView &key, Input first, Input last); + + /// @brief Push multiple elements to the end of the list. + /// @param key Key where the list is stored. + /// @param il Initializer list of elements to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpush + template + long long rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + /// @brief Push an element to the end of the list, only if the list already exists. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpushx + long long rpushx(const StringView &key, const StringView &val); + + // HASH commands. + + /// @brief Remove the given field from hash. + /// @param key Key where the hash is stored. + /// @param field Field to be removed. + /// @return Whether the field has been removed. + /// @retval 1 If the field exists, and has been removed. + /// @retval 0 If the field does not exist. + /// @see https://redis.io/commands/hdel + long long hdel(const StringView &key, const StringView &field); + + /// @brief Remove multiple fields from hash. + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field to be removed. + /// @param last Off-the-end iterator to the given field range. + /// @return Number of fields that has been removed. + /// @see https://redis.io/commands/hdel + template + long long hdel(const StringView &key, Input first, Input last); + + /// @brief Remove multiple fields from hash. + /// @param key Key where the hash is stored. + /// @param il Initializer list of fields. + /// @return Number of fields that has been removed. + /// @see https://redis.io/commands/hdel + template + long long hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + /// @brief Check if the given field exists in hash. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @return Whether the field exists. + /// @retval true If the field exists in hash. + /// @retval false If the field does not exist. + /// @see https://redis.io/commands/hexists + bool hexists(const StringView &key, const StringView &field); + + /// @brief Get the value of the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @return Value of the given field. + /// @note If field does not exist, `hget` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/hget + OptionalString hget(const StringView &key, const StringView &field); + + /// @brief Get all field-value pairs of the given hash. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map results; + /// // Save all field-value pairs of a Redis hash to an unordered_map. + /// redis.hgetall("hash", std::inserter(results, results.begin())); + /// @endcode + /// @param key Key where the hash is stored. + /// @param output Output iterator to the destination where the result is saved. + /// @note It's always a bad idea to call `hgetall` on a large hash, since it will block Redis. + /// @see `Redis::hscan` + /// @see https://redis.io/commands/hgetall + template + void hgetall(const StringView &key, Output output); + + /// @brief Increment the integer stored at the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param increment Increment. + /// @return The value of the field after the increment. + /// @see https://redis.io/commands/hincrby + long long hincrby(const StringView &key, const StringView &field, long long increment); + + /// @brief Increment the floating point number stored at the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param increment Increment. + /// @return The value of the field after the increment. + /// @see https://redis.io/commands/hincrbyfloat + double hincrbyfloat(const StringView &key, const StringView &field, double increment); + + /// @brief Get all fields of the given hash. + /// @param key Key where the hash is stored. + /// @param output Output iterator to the destination where the result is saved. + /// @note It's always a bad idea to call `hkeys` on a large hash, since it will block Redis. + /// @see `Redis::hscan` + /// @see https://redis.io/commands/hkeys + template + void hkeys(const StringView &key, Output output); + + /// @brief Get the number of fields of the given hash. + /// @param key Key where the hash is stored. + /// @return Number of fields. + /// @see https://redis.io/commands/hlen + long long hlen(const StringView &key); + + /// @brief Get values of multiple fields. + /// + /// Example: + /// @code{.cpp} + /// std::vector fields = {"f1", "f2"}; + /// std::vector vals; + /// redis.hmget("hash", fields.begin(), fields.end(), std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "field not exist" << std::endl; + /// } + /// @endcode + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field. + /// @param last Off-the-end iterator to the given field range. + /// @param output Output iterator to the destination where the result is saved. + /// @note The destination should be a container of `OptionalString` type, + /// since the given field might not exist (in this case, the value of the corresponding + /// field is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/hmget + template + void hmget(const StringView &key, Input first, Input last, Output output); + + /// @brief Get values of multiple fields. + /// + /// Example: + /// @code{.cpp} + /// std::vector vals; + /// redis.hmget("hash", {"f1", "f2"}, std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "field not exist" << std::endl; + /// } + /// @endcode + /// @param key Key where the hash is stored. + /// @param il Initializer list of fields. + /// @param output Output iterator to the destination where the result is saved. + /// @note The destination should be a container of `OptionalString` type, + /// since the given field might not exist (in this case, the value of the corresponding + /// field is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/hmget + template + void hmget(const StringView &key, std::initializer_list il, Output output) { + hmget(key, il.begin(), il.end(), output); + } + + /// @brief Set multiple field-value pairs of the given hash. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map m = {{"f1", "v1"}, {"f2", "v2"}}; + /// redis.hmset("hash", m.begin(), m.end()); + /// @endcode + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field-value pair. + /// @param last Off-the-end iterator to the range. + /// @see https://redis.io/commands/hmset + template + void hmset(const StringView &key, Input first, Input last); + + /// @brief Set multiple field-value pairs of the given hash. + /// + /// Example: + /// @code{.cpp} + /// redis.hmset("hash", {std::make_pair("f1", "v1"), std::make_pair("f2", "v2")}); + /// @endcode + /// @param key Key where the hash is stored. + /// @param il Initializer list of field-value pairs. + /// @see https://redis.io/commands/hmset + template + void hmset(const StringView &key, std::initializer_list il) { + hmset(key, il.begin(), il.end()); + } + + /// @brief Scan fields of the given hash matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::unordered_map kvs; + /// while (true) { + /// cursor = redis.hscan("hash", cursor, "pattern:*", 10, std::inserter(kvs, kvs.begin())); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan fields of the given hash matching the given pattern. + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan all fields of the given hash. + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + long long count, + Output output); + + /// @brief Scan all fields of the given hash. + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + Output output); + + /// @brief Set hash field to value. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param val Value. + /// @return Whether the given field is a new field. + /// @retval true If the given field didn't exist, and a new field has been added. + /// @retval false If the given field already exists, and its value has been overwritten. + /// @note When `hset` returns false, it does not mean that the method failed to set the field. + /// Instead, it means that the field already exists, and we've overwritten its value. + /// If `hset` fails, it will throw an exception of `Exception` type. + /// @see https://github.com/sewenew/redis-plus-plus/issues/9 + /// @see https://redis.io/commands/hset + bool hset(const StringView &key, const StringView &field, const StringView &val); + + /// @brief Set hash field to value. + /// @param key Key where the hash is stored. + /// @param item The field-value pair to be set. + /// @return Whether the given field is a new field. + /// @retval true If the given field didn't exist, and a new field has been added. + /// @retval false If the given field already exists, and its value has been overwritten. + /// @note When `hset` returns false, it does not mean that the method failed to set the field. + /// Instead, it means that the field already exists, and we've overwritten its value. + /// If `hset` fails, it will throw an exception of `Exception` type. + /// @see https://github.com/sewenew/redis-plus-plus/issues/9 + /// @see https://redis.io/commands/hset + bool hset(const StringView &key, const std::pair &item); + + /// @brief Set multiple fields of the given hash. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map m = {{"f1", "v1"}, {"f2", "v2"}}; + /// redis.hset("hash", m.begin(), m.end()); + /// @endcode + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field to be set. + /// @param last Off-the-end iterator to the given range. + /// @return Number of fields that have been added, i.e. fields that not existed before. + /// @see https://redis.io/commands/hset + template + auto hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, long long>::type; + + /// @brief Set multiple fields of the given hash. + /// + /// Example: + /// @code{.cpp} + /// redis.hset("hash", {std::make_pair("f1", "v1"), std::make_pair("f2", "v2")}); + /// @endcode + /// @param key Key where the hash is stored. + /// @param il Initializer list of field-value pairs. + /// @return Number of fields that have been added, i.e. fields that not existed before. + /// @see https://redis.io/commands/hset + template + long long hset(const StringView &key, std::initializer_list il) { + return hset(key, il.begin(), il.end()); + } + + /// @brief Set hash field to value, only if the given field does not exist. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param val Value. + /// @return Whether the field has been set. + /// @retval true If the field has been set. + /// @retval false If failed to set the field, i.e. the field already exists. + /// @see https://redis.io/commands/hsetnx + bool hsetnx(const StringView &key, const StringView &field, const StringView &val); + + /// @brief Set hash field to value, only if the given field does not exist. + /// @param key Key where the hash is stored. + /// @param item The field-value pair to be set. + /// @return Whether the field has been set. + /// @retval true If the field has been set. + /// @retval false If failed to set the field, i.e. the field already exists. + /// @see https://redis.io/commands/hsetnx + bool hsetnx(const StringView &key, const std::pair &item); + + /// @brief Get the length of the string stored at the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @return Length of the string. + /// @see https://redis.io/commands/hstrlen + long long hstrlen(const StringView &key, const StringView &field); + + /// @brief Get values of all fields stored at the given hash. + /// @param key Key where the hash is stored. + /// @param output Output iterator to the destination where the result is saved. + /// @note It's always a bad idea to call `hvals` on a large hash, since it might block Redis. + /// @see `Redis::hscan` + /// @see https://redis.io/commands/hvals + template + void hvals(const StringView &key, Output output); + + // SET commands. + + /// @brief Add a member to the given set. + /// @param key Key where the set is stored. + /// @param member Member to be added. + /// @return Whether the given member is a new member. + /// @retval 1 The member did not exist before, and it has been added now. + /// @retval 0 The member already exists before this operation. + /// @see https://redis.io/commands/sadd + long long sadd(const StringView &key, const StringView &member); + + /// @brief Add multiple members to the given set. + /// @param key Key where the set is stored. + /// @param first Iterator to the first member to be added. + /// @param last Off-the-end iterator to the member range. + /// @return Number of new members that have been added, i.e. members did not exist before. + /// @see https://redis.io/commands/sadd + template + long long sadd(const StringView &key, Input first, Input last); + + /// @brief Add multiple members to the given set. + /// @param key Key where the set is stored. + /// @param il Initializer list of members to be added. + /// @return Number of new members that have been added, i.e. members did not exist before. + /// @see https://redis.io/commands/sadd + template + long long sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + /// @brief Get the number of members in the set. + /// @param key Key where the set is stored. + /// @return Number of members. + /// @see https://redis.io/commands/scard + long long scard(const StringView &key); + + /// @brief Get the difference between the first set and all successive sets. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to the range. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sdiff + // TODO: `void sdiff(const StringView &key, Input first, Input last, Output output)` is better. + template + void sdiff(Input first, Input last, Output output); + + /// @brief Get the difference between the first set and all successive sets. + /// @param il Initializer list of sets. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sdiff + template + void sdiff(std::initializer_list il, Output output) { + sdiff(il.begin(), il.end(), output); + } + + /// @brief Copy set stored at `key` to `destination`. + /// @param destination Key of the destination set. + /// @param key Key of the source set. + /// @return Number of members of the set. + /// @see https://redis.io/commands/sdiffstore + long long sdiffstore(const StringView &destination, const StringView &key); + + /// @brief Same as `sdiff`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to set range. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sdiffstore + template + long long sdiffstore(const StringView &destination, + Input first, + Input last); + + /// @brief Same as `sdiff`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param il Initializer list of sets. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sdiffstore + template + long long sdiffstore(const StringView &destination, + std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + /// @brief Get the intersection between the first set and all successive sets. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to the range. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sinter + // TODO: `void sinter(const StringView &key, Input first, Input last, Output output)` is better. + template + void sinter(Input first, Input last, Output output); + + /// @brief Get the intersection between the first set and all successive sets. + /// @param il Initializer list of sets. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sinter + template + void sinter(std::initializer_list il, Output output) { + sinter(il.begin(), il.end(), output); + } + + /// @brief Copy set stored at `key` to `destination`. + /// @param destination Key of the destination set. + /// @param key Key of the source set. + /// @return Number of members of the set. + /// @see https://redis.io/commands/sinter + long long sinterstore(const StringView &destination, const StringView &key); + + /// @brief Same as `sinter`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to set range. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sinter + template + long long sinterstore(const StringView &destination, + Input first, + Input last); + + /// @brief Same as `sinter`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param il Initializer list of sets. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sinter + template + long long sinterstore(const StringView &destination, + std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + /// @brief Test if `member` exists in the set stored at key. + /// @param key Key where the set is stored. + /// @param member Member to be checked. + /// @return Whether `member` exists in the set. + /// @retval true If it exists in the set. + /// @retval false If it does not exist in the set, or the given key does not exist. + /// @see https://redis.io/commands/sismember + bool sismember(const StringView &key, const StringView &member); + + /// @brief Get all members in the given set. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_set members1; + /// redis.smembers("set", std::inserter(members1, members1.begin())); + /// std::vector members2; + /// redis.smembers("set", std::back_inserter(members2)); + /// @endcode + /// @param key Key where the set is stored. + /// @param output Iterator to the destination where the result is saved. + /// @see https://redis.io/commands/smembers + template + void smembers(const StringView &key, Output output); + + /// @brief Move `member` from one set to another. + /// @param source Key of the set in which the member currently exists. + /// @param destination Key of the destination set. + /// @return Whether the member has been moved. + /// @retval true If the member has been moved. + /// @retval false If `member` does not exist in `source`. + /// @see https://redis.io/commands/smove + bool smove(const StringView &source, + const StringView &destination, + const StringView &member); + + /// @brief Remove a random member from the set. + /// @param key Key where the set is stored. + /// @return The popped member. + /// @note If the set is empty, `spop` returns `OptionalString{}` (`std::nullopt`). + /// @see `Redis::srandmember` + /// @see https://redis.io/commands/spop + OptionalString spop(const StringView &key); + + /// @brief Remove multiple random members from the set. + /// + /// Example: + /// @code{.cpp} + /// std::vector members; + /// redis.spop("set", 10, std::back_inserter(members)); + /// @endcode + /// @param key Key where the set is stored. + /// @param count Number of members to be popped. + /// @param output Output iterator to the destination where the result is saved. + /// @note The number of popped members might be less than `count`. + /// @see `Redis::srandmember` + /// @see https://redis.io/commands/spop + template + void spop(const StringView &key, long long count, Output output); + + /// @brief Get a random member of the given set. + /// @param key Key where the set is stored. + /// @return A random member. + /// @note If the set is empty, `srandmember` returns `OptionalString{}` (`std::nullopt`). + /// @note This method won't remove the member from the set. + /// @see `Redis::spop` + /// @see https://redis.io/commands/srandmember + OptionalString srandmember(const StringView &key); + + /// @brief Get multiple random members of the given set. + /// @param key Key where the set is stored. + /// @param count Number of members to be returned. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method won't remove members from the set. + /// @see `Redis::spop` + /// @see https://redis.io/commands/srandmember + template + void srandmember(const StringView &key, long long count, Output output); + + /// @brief Remove a member from set. + /// @param key Key where the set is stored. + /// @param member Member to be removed. + /// @return Whether the member has been removed. + /// @retval 1 If the given member exists, and has been removed. + /// @retval 0 If the given member does not exist. + /// @see https://redis.io/commands/srem + long long srem(const StringView &key, const StringView &member); + + /// @brief Remove multiple members from set. + /// @param key Key where the set is stored. + /// @param first Iterator to the first member to be removed. + /// @param last Off-the-end iterator to the range. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/srem + template + long long srem(const StringView &key, Input first, Input last); + + /// @brief Remove multiple members from set. + /// @param key Key where the set is stored. + /// @param il Initializer list of members to be removed. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/srem + template + long long srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + /// @brief Scan members of the set matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::unordered_set members; + /// while (true) { + /// cursor = redis.sscan("set", cursor, "pattern:*", + /// 10, std::inserter(members, members.begin())); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan members of the set matching the given pattern. + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan all members of the given set. + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + long long count, + Output output); + + /// @brief Scan all members of the given set. + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + Output output); + + /// @brief Get the union between the first set and all successive sets. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to the range. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sunion + // TODO: `void sunion(const StringView &key, Input first, Input last, Output output)` is better. + template + void sunion(Input first, Input last, Output output); + + /// @brief Get the union between the first set and all successive sets. + /// @param il Initializer list of sets. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sunion + template + void sunion(std::initializer_list il, Output output) { + sunion(il.begin(), il.end(), output); + } + + /// @brief Copy set stored at `key` to `destination`. + /// @param destination Key of the destination set. + /// @param key Key of the source set. + /// @return Number of members of the set. + /// @see https://redis.io/commands/sunionstore + long long sunionstore(const StringView &destination, const StringView &key); + + /// @brief Same as `sunion`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to set range. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sunionstore + template + long long sunionstore(const StringView &destination, Input first, Input last); + + /// @brief Same as `sunion`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param il Initializer list of sets. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sunionstore + template + long long sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + /// @brief Pop the member with highest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the highest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + auto bzpopmax(const StringView &key, long long timeout) + -> Optional>; + + /// @brief Pop the member with highest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the highest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(Input first, Input last, long long timeout) + -> Optional>; + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + /// @brief Pop the member with lowest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + auto bzpopmin(const StringView &key, long long timeout) + -> Optional>; + + /// @brief Pop the member with lowest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(Input first, Input last, long long timeout) + -> Optional>; + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + /// @brief Add or update a member with score to sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member to be added. + /// @param score Score of the member. + /// @param type Options for zadd command: + /// - UpdateType::EXIST: Add the member only if it already exists. + /// - UpdateType::NOT_EXIST: Add the member only if it does not exist. + /// - UpdateType::ALWAYS: Always add the member no matter whether it exists. + /// @param changed Whether change the return value from number of newly added member to + /// number of members changed (i.e. added and updated). + /// @return Number of added members or number of added and updated members depends on `changed`. + /// @note We don't support the INCR option, because in this case, the return value of zadd + /// command is NOT of type long long. However, you can always use the generic interface + /// to send zadd command with INCR option: + /// `auto score = redis.command("ZADD", "key", "XX", "INCR", 10, "mem");` + /// @see `UpdateType` + /// @see https://redis.io/commands/zadd + long long zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + /// @brief Add or update multiple members with score to sorted set. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map m = {{"m1", 1.2}, {"m2", 2.3}}; + /// redis.zadd("zset", m.begin(), m.end()); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param first Iterator to the first member-score pair. + /// @param last Off-the-end iterator to the member-score pairs range. + /// @param type Options for zadd command: + /// - UpdateType::EXIST: Add the member only if it already exists. + /// - UpdateType::NOT_EXIST: Add the member only if it does not exist. + /// - UpdateType::ALWAYS: Always add the member no matter whether it exists. + /// @param changed Whether change the return value from number of newly added member to + /// number of members changed (i.e. added and updated). + /// @return Number of added members or number of added and updated members depends on `changed`. + /// @note We don't support the INCR option, because in this case, the return value of zadd + /// command is NOT of type long long. However, you can always use the generic interface + /// to send zadd command with INCR option: + /// `auto score = redis.command("ZADD", "key", "XX", "INCR", 10, "mem");` + /// @see `UpdateType` + /// @see https://redis.io/commands/zadd + template + long long zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + /// @brief Add or update multiple members with score to sorted set. + /// + /// Example: + /// @code{.cpp} + /// redis.zadd("zset", {std::make_pair("m1", 1.4), std::make_pair("m2", 2.3)}); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param first Iterator to the first member-score pair. + /// @param last Off-the-end iterator to the member-score pairs range. + /// @param type Options for zadd command: + /// - UpdateType::EXIST: Add the member only if it already exists. + /// - UpdateType::NOT_EXIST: Add the member only if it does not exist. + /// - UpdateType::ALWAYS: Always add the member no matter whether it exists. + /// @param changed Whether change the return value from number of newly added member to + /// number of members changed (i.e. added and updated). + /// @return Number of added members or number of added and updated members depends on `changed`. + /// @note We don't support the INCR option, because in this case, the return value of zadd + /// command is NOT of type long long. However, you can always use the generic interface + /// to send zadd command with INCR option: + /// `auto score = redis.command("ZADD", "key", "XX", "INCR", 10, "mem");` + /// @see `UpdateType` + /// @see https://redis.io/commands/zadd + template + long long zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + /// @brief Get the number of members in the sorted set. + /// @param key Key where the sorted set is stored. + /// @return Number of members in the sorted set. + /// @see https://redis.io/commands/zcard + long long zcard(const StringView &key); + + /// @brief Get the number of members with score between a min-max score range. + /// + /// Example: + /// @code{.cpp} + /// // Count members with score between (2.3, 5] + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::LEFT_OPEN)); + /// // Count members with score between [2.3, 5) + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::RIGHT_OPEN)); + /// // Count members with score between (2.3, 5) + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::OPEN)); + /// // Count members with score between [2.3, 5] + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::CLOSED)); + /// // Count members with score between [2.3, +inf) + /// redis.zcount("zset", LeftBoundedInterval(2.3, BoundType::RIGHT_OPEN)); + /// // Count members with score between (2.3, +inf) + /// redis.zcount("zset", LeftBoundedInterval(2.3, BoundType::OPEN)); + /// // Count members with score between (-inf, 5] + /// redis.zcount("zset", RightBoundedInterval(5, BoundType::LEFT_OPEN)); + /// // Count members with score between (-inf, 5) + /// redis.zcount("zset", RightBoundedInterval(5, BoundType::OPEN)); + /// // Count members with score between (-inf, +inf) + /// redis.zcount("zset", UnboundedInterval{}); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval The min-max score range. + /// @return Number of members with score between a min-max score range. + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see https://redis.io/commands/zcount + // TODO: add a string version of Interval: zcount("key", "(2.3", "5"). + template + long long zcount(const StringView &key, const Interval &interval); + + /// @brief Increment the score of given member. + /// @param key Key where the sorted set is stored. + /// @param increment Increment. + /// @param member Member. + /// @return The score of the member after the operation. + /// @see https://redis.io/commands/zincrby + double zincrby(const StringView &key, double increment, const StringView &member); + + /// @brief Copy a sorted set to another one with the scores being multiplied by a factor. + /// @param destination Key of the destination sorted set. + /// @param key Key of the source sorted set. + /// @param weight Weight to be multiplied to the score of each member. + /// @return The number of members in the sorted set. + /// @note There's no aggregation type parameter for single key overload, since these 3 types + /// have the same effect. + /// @see `Redis::zunionstore` + /// @see https://redis.io/commands/zinterstore + long long zinterstore(const StringView &destination, const StringView &key, double weight); + + /// @brief Get intersection of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// std::vector keys = {"k1", "k2", "k3"}; + /// redis.zinterstore("destination", keys.begin(), keys.end()); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// std::vector> keys_with_weights = {{"k1", 1}, {"k2", 2}}; + /// redis.zinterstore("destination", keys_with_weights.begin(), + /// keys_with_weights.end(), Aggregation::MIN); + /// // NOTE: `keys_with_weights` can also be of type `std::unordered_map`. + /// // However, it will be slower than std::vector>, since we use + /// // `std::distance(first, last)` to calculate the *numkeys* parameter. + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param first Iterator to the first sorted set (might with weight). + /// @param last Off-the-end iterator to the sorted set range. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `Input` is an + /// iterator to a container of `std::string`, we use the default weight, i.e. 1, and send + /// *ZINTERSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `Input` is an iterator to a container of `std::pair`, + /// i.e. key-weight pair, we send the command with the given weights: + /// *ZINTERSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zunionstore` + /// @see https://redis.io/commands/zinterstore + template + long long zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + /// @brief Get intersection of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// redis.zinterstore("destination", {"k1", "k2"}); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// redis.zinterstore("destination", + /// {std::make_pair("k1", 1), std::make_pair("k2", 2)}, Aggregation::MIN); + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param il Initializer list of sorted set. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `T` is + /// of type `std::string`, we use the default weight, i.e. 1, and send + /// *ZINTERSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `T` is of type `std::pair`, i.e. key-weight pair, + /// we send the command with the given weights: + /// *ZINTERSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zunionstore` + /// @see https://redis.io/commands/zinterstore + template + long long zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + /// @brief Get the number of members between a min-max range in lexicographical order. + /// + /// Example: + /// @code{.cpp} + /// // Count members between (abc, abd] + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::LEFT_OPEN)); + /// // Count members between [abc, abd) + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::RIGHT_OPEN)); + /// // Count members between (abc, abd) + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::OPEN)); + /// // Count members between [abc, abd] + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::CLOSED)); + /// // Count members between [abc, +inf) + /// redis.zlexcount("zset", LeftBoundedInterval("abc", BoundType::RIGHT_OPEN)); + /// // Count members between (abc, +inf) + /// redis.zlexcount("zset", LeftBoundedInterval("abc", BoundType::OPEN)); + /// // Count members between (-inf, "abd"] + /// redis.zlexcount("zset", RightBoundedInterval("abd", BoundType::LEFT_OPEN)); + /// // Count members between (-inf, "abd") + /// redis.zlexcount("zset", RightBoundedInterval("abd", BoundType::OPEN)); + /// // Count members between (-inf, +inf) + /// redis.zlexcount("zset", UnboundedInterval{}); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval The min-max range in lexicographical order. + /// @return Number of members between a min-max range in lexicographical order. + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see https://redis.io/commands/zlexcount + // TODO: add a string version of Interval: zlexcount("key", "(abc", "abd"). + template + long long zlexcount(const StringView &key, const Interval &interval); + + /// @brief Pop the member with highest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @return Member-score pair with the highest score. + /// @note If sorted set is empty `zpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::bzpopmax` + /// @see https://redis.io/commands/zpopmax + Optional> zpopmax(const StringView &key); + + /// @brief Pop multiple members with highest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @param count Number of members to be popped. + /// @param output Output iterator to the destination where the result is saved. + /// @note The number of returned members might be less than `count`. + /// @see `Redis::bzpopmax` + /// @see https://redis.io/commands/zpopmax + template + void zpopmax(const StringView &key, long long count, Output output); + + /// @brief Pop the member with lowest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @return Member-score pair with the lowest score. + /// @note If sorted set is empty `zpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::bzpopmin` + /// @see https://redis.io/commands/zpopmin + Optional> zpopmin(const StringView &key); + + /// @brief Pop multiple members with lowest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @param count Number of members to be popped. + /// @param output Output iterator to the destination where the result is saved. + /// @note The number of returned members might be less than `count`. + /// @see `Redis::bzpopmin` + /// @see https://redis.io/commands/zpopmin + template + void zpopmin(const StringView &key, long long count, Output output); + + /// @brief Get a range of members by rank (ordered from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// // send *ZRANGE* command without the *WITHSCORES* option: + /// std::vector result; + /// redis.zrange("zset", 0, -1, std::back_inserter(result)); + /// // send command with *WITHSCORES* option: + /// std::vector> with_score; + /// redis.zrange("zset", 0, -1, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param start Start rank. Inclusive and can be negative. + /// @param stop Stop rank. Inclusive and can be negative. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZRANGE key start stop* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZRANGE key start stop WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @see `Redis::zrevrange` + /// @see https://redis.io/commands/zrange + template + void zrange(const StringView &key, long long start, long long stop, Output output); + + /// @brief Get a range of members by lexicographical order (from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Get members between [abc, abd]. + /// redis.zrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `Redis::zrevrangebylex` + /// @see https://redis.io/commands/zrangebylex + template + void zrangebylex(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by lexicographical order (from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Limit the result to at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members between [abc, abd]. + /// redis.zrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// opts, std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `LimitOptions` + /// @see `Redis::zrevrangebylex` + /// @see https://redis.io/commands/zrangebylex + template + void zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get a range of members by score (ordered from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Get members whose score between (3, 6). + /// redis.zrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf). + /// redis.zrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrevrangebyscore` + /// @see https://redis.io/commands/zrangebyscore + template + void zrangebyscore(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by score (ordered from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Only return at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members whose score between (3, 6). + /// redis.zrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// opts, std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf). + /// redis.zrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// opts, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrevrangebyscore` + /// @see https://redis.io/commands/zrangebyscore + template + void zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get the rank (from low to high) of the given member in the sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member. + /// @return The rank of the given member. + /// @note If the member does not exist, `zrank` returns `OptionalLongLong{}` (`std::nullopt`). + /// @see https://redis.io/commands/zrank + OptionalLongLong zrank(const StringView &key, const StringView &member); + + /// @brief Remove the given member from sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member to be removed. + /// @return Whether the member has been removed. + /// @retval 1 If the member exists, and has been removed. + /// @retval 0 If the member does not exist. + /// @see https://redis.io/commands/zrem + long long zrem(const StringView &key, const StringView &member); + + /// @brief Remove multiple members from sorted set. + /// @param key Key where the sorted set is stored. + /// @param first Iterator to the first member. + /// @param last Off-the-end iterator to the given range. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/zrem + template + long long zrem(const StringView &key, Input first, Input last); + + /// @brief Remove multiple members from sorted set. + /// @param key Key where the sorted set is stored. + /// @param il Initializer list of members to be removed. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/zrem + template + long long zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + /// @brief Remove members in the given range of lexicographical order. + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @return Number of members removed. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see https://redis.io/commands/zremrangebylex + template + long long zremrangebylex(const StringView &key, const Interval &interval); + + /// @brief Remove members in the given range ordered by rank. + /// @param key Key where the sorted set is stored. + /// @param start Start rank. + /// @param stop Stop rank. + /// @return Number of members removed. + /// @see https://redis.io/commands/zremrangebyrank + long long zremrangebyrank(const StringView &key, long long start, long long stop); + + /// @brief Remove members in the given range ordered by score. + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @return Number of members removed. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see https://redis.io/commands/zremrangebyscore + template + long long zremrangebyscore(const StringView &key, const Interval &interval); + + /// @brief Get a range of members by rank (ordered from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// // send *ZREVRANGE* command without the *WITHSCORES* option: + /// std::vector result; + /// redis.zrevrange("key", 0, -1, std::back_inserter(result)); + /// // send command with *WITHSCORES* option: + /// std::vector> with_score; + /// redis.zrevrange("key", 0, -1, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param start Start rank. Inclusive and can be negative. + /// @param stop Stop rank. Inclusive and can be negative. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZREVRANGE key start stop* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZREVRANGE key start stop WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @see `Redis::zrange` + /// @see https://redis.io/commands/zrevrange + template + void zrevrange(const StringView &key, long long start, long long stop, Output output); + + /// @brief Get a range of members by lexicographical order (from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Get members between [abc, abd] in reverse order. + /// redis.zrevrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `Redis::zrangebylex` + /// @see https://redis.io/commands/zrevrangebylex + template + void zrevrangebylex(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by lexicographical order (from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Limit the result to at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members between [abc, abd] in reverse order. + /// redis.zrevrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// opts, std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `LimitOptions` + /// @see `Redis::zrangebylex` + /// @see https://redis.io/commands/zrevrangebylex + template + void zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get a range of members by score (ordered from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZREVRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Get members whose score between (3, 6) in reverse order. + /// redis.zrevrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf) in reverse order. + /// redis.zrevrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZREVRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZREVRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrangebyscore` + /// @see https://redis.io/commands/zrevrangebyscore + template + void zrevrangebyscore(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by score (ordered from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZREVRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Only return at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members whose score between (3, 6) in reverse order. + /// redis.zrevrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// opts, std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf) in reverse order. + /// redis.zrevrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// opts, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZREVRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZREVRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrangebyscore` + /// @see https://redis.io/commands/zrevrangebyscore + template + void zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get the rank (from high to low) of the given member in the sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member. + /// @return The rank of the given member. + /// @note If the member does not exist, `zrevrank` returns `OptionalLongLong{}` (`std::nullopt`). + /// @see https://redis.io/commands/zrevrank + OptionalLongLong zrevrank(const StringView &key, const StringView &member); + + /// @brief Scan members of the given sorted set matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::vector> members; + /// while (true) { + /// cursor = redis.zscan("zset", cursor, "pattern:*", + /// 10, std::back_inserter(members)); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of members to be scanned. + /// @param count A hint for how many members to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan members of the given sorted set matching the given pattern. + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of members to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan all members of the given sorted set. + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param count A hint for how many members to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + long long count, + Output output); + + /// @brief Scan all members of the given sorted set. + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + Output output); + + /// @brief Get the score of the given member. + /// @param key Key where the sorted set is stored. + /// @param member Member. + /// @return The score of the member. + /// @note If member does not exist, `zscore` returns `OptionalDouble{}` (`std::nullopt`). + /// @see https://redis.io/commands/zscore + OptionalDouble zscore(const StringView &key, const StringView &member); + + /// @brief Copy a sorted set to another one with the scores being multiplied by a factor. + /// @param destination Key of the destination sorted set. + /// @param key Key of the source sorted set. + /// @param weight Weight to be multiplied to the score of each member. + /// @return The number of members in the sorted set. + /// @note There's no aggregation type parameter for single key overload, since these 3 types + /// have the same effect. + /// @see `Redis::zinterstore` + /// @see https://redis.io/commands/zinterstore + long long zunionstore(const StringView &destination, const StringView &key, double weight); + + /// @brief Get union of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// std::vector keys = {"k1", "k2", "k3"}; + /// redis.zunionstore("destination", keys.begin(), keys.end()); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// std::vector> keys_with_weights = {{"k1", 1}, {"k2", 2}}; + /// redis.zunionstore("destination", keys_with_weights.begin(), + /// keys_with_weights.end(), Aggregation::MIN); + /// // NOTE: `keys_with_weights` can also be of type `std::unordered_map`. + /// // However, it will be slower than std::vector>, since we use + /// // `std::distance(first, last)` to calculate the *numkeys* parameter. + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param first Iterator to the first sorted set (might with weight). + /// @param last Off-the-end iterator to the sorted set range. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `Input` is an + /// iterator to a container of `std::string`, we use the default weight, i.e. 1, and send + /// *ZUNIONSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `Input` is an iterator to a container of `std::pair`, + /// i.e. key-weight pair, we send the command with the given weights: + /// *ZUNIONSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zinterstore` + /// @see https://redis.io/commands/zunionstore + template + long long zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + /// @brief Get union of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// redis.zunionstore("destination", {"k1", "k2"}); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// redis.zunionstore("destination", + /// {std::make_pair("k1", 1), std::make_pair("k2", 2)}, Aggregation::MIN); + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param il Initializer list of sorted set. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `T` is + /// of type `std::string`, we use the default weight, i.e. 1, and send + /// *ZUNIONSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `T` is of type `std::pair`, i.e. key-weight pair, + /// we send the command with the given weights: + /// *ZUNIONSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zinterstore` + /// @see https://redis.io/commands/zunionstore + template + long long zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + /// @brief Add the given element to a hyperloglog. + /// @param key Key of the hyperloglog. + /// @param element Element to be added. + /// @return Whether any of hyperloglog's internal register has been altered. + /// @retval true If at least one internal register has been altered. + /// @retval false If none of internal registers has been altered. + /// @note When `pfadd` returns false, it does not mean that this method failed to add + /// an element to the hyperloglog. Instead it means that the internal registers + /// were not altered. If `pfadd` fails, it will throw an exception of `Exception` type. + /// @see https://redis.io/commands/pfadd + bool pfadd(const StringView &key, const StringView &element); + + /// @brief Add the given elements to a hyperloglog. + /// @param key Key of the hyperloglog. + /// @param first Iterator to the first element. + /// @param last Off-the-end iterator to the given range. + /// @return Whether any of hyperloglog's internal register has been altered. + /// @retval true If at least one internal register has been altered. + /// @retval false If none of internal registers has been altered. + /// @note When `pfadd` returns false, it does not mean that this method failed to add + /// an element to the hyperloglog. Instead it means that the internal registers + /// were not altered. If `pfadd` fails, it will throw an exception of `Exception` type. + /// @see https://redis.io/commands/pfadd + template + bool pfadd(const StringView &key, Input first, Input last); + + /// @brief Add the given elements to a hyperloglog. + /// @param key Key of the hyperloglog. + /// @param il Initializer list of elements to be added. + /// @return Whether any of hyperloglog's internal register has been altered. + /// @retval true If at least one internal register has been altered. + /// @retval false If none of internal registers has been altered. + /// @note When `pfadd` returns false, it does not mean that this method failed to add + /// an element to the hyperloglog. Instead it means that the internal registers + /// were not altered. If `pfadd` fails, it will throw an exception of `Exception` type. + /// @see https://redis.io/commands/pfadd + template + bool pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + long long pfcount(const StringView &key); + + template + long long pfcount(Input first, Input last); + + template + long long pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + void pfmerge(const StringView &destination, const StringView &key); + + template + void pfmerge(const StringView &destination, Input first, Input last); + + template + void pfmerge(const StringView &destination, std::initializer_list il) { + pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + long long geoadd(const StringView &key, + const std::tuple &member); + + template + long long geoadd(const StringView &key, + Input first, + Input last); + + template + long long geoadd(const StringView &key, + std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + OptionalDouble geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M); + + OptionalString geohash(const StringView &key, const StringView &member); + + template + void geohash(const StringView &key, Input first, Input last, Output output); + + template + void geohash(const StringView &key, std::initializer_list il, Output output) { + geohash(key, il.begin(), il.end(), output); + } + + Optional> geopos(const StringView &key, const StringView &member); + + template + void geopos(const StringView &key, Input first, Input last, Output output); + + template + void geopos(const StringView &key, std::initializer_list il, Output output) { + geopos(key, il.begin(), il.end(), output); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + + /// @brief Get members in geo range, i.e. a circle, and store them in a sorted set. + /// @param key Key of the GEO set. + /// @param loc Location encoded with pair. + /// @param radius Radius of the range. + /// @param unit Radius unit. + /// @param destination Key of the destination sorted set. + /// @param store_dist Whether store distance info instead of geo info to destination. + /// @param count Limit the first N members. + /// @return Number of members stored in destination. + /// @note Before Redis 6.2.6, if key does not exist, returns `OptionalLongLong{}` (`std::nullopt`). + /// Since Redis 6.2.6, if key does not exist, returns 0. + /// @see `GeoUnit` + /// @see `Redis::georadiusbymember` + /// @see https://redis.io/commands/georadius + OptionalLongLong georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // If *output* is an iterator of a container of string, we send *GEORADIUS* command + // without any options and only get the members in the specified geo range. + // If *output* is an iterator of a container of a tuple, the type of the tuple decides + // options we send with the *GEORADIUS* command. If the tuple has an element of type + // double, we send the *WITHDIST* option. If it has an element of type string, we send + // the *WITHHASH* option. If it has an element of type pair, we send + // the *WITHCOORD* option. For example: + // + // The following code only gets the members in range, i.e. without any option. + // + // vector members; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(members)) + // + // The following code sends the command with *WITHDIST* option. + // + // vector> with_dist; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist)) + // + // The following code sends the command with *WITHDIST* and *WITHHASH* options. + // + // vector> with_dist_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_hash)) + // + // The following code sends the command with *WITHDIST*, *WITHCOORD* and *WITHHASH* options. + // + // vector, string>> with_dist_coord_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_coord_hash)) + // + // This also applies to *GEORADIUSBYMEMBER*. + template + void georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + /// @brief Get members in geo range, i.e. a circle, and store them in a sorted set. + /// @param key Key of the GEO set. + /// @param member Member which is the center of the circle. + /// @param radius Radius of the range. + /// @param unit Radius unit. + /// @param destination Key of the destination sorted set. + /// @param store_dist Whether store distance info instead of geo info to destination. + /// @param count Limit the first N members. + /// @return Number of members stored in destination. + /// @note Before Redis 6.2.6, if key does not exist, returns `OptionalLongLong{}` (`std::nullopt`). + /// Since Redis 6.2.6, if key does not exist, returns 0. + /// @note If member does not exist, throw an `ReplyError`. + /// @see `GeoUnit` + /// @see `Redis::georadius` + /// @see https://redis.io/commands/georadiusbymember + OptionalLongLong georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // See comments on *GEORADIUS*. + template + void georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + // SCRIPTING commands. + + template + Result eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + Result evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + /// @brief Check if the given script exists. + /// @param sha1 SHA1 digest of the script. + /// @return Whether the script exists. + /// @retval true If the script exists. + /// @retval false If the script does not exist. + /// @see https://redis.io/commands/script-exists + bool script_exists(const StringView &sha1); + + template + void script_exists(Input first, Input last, Output output); + + template + void script_exists(std::initializer_list il, Output output) { + script_exists(il.begin(), il.end(), output); + } + + void script_flush(); + + void script_kill(); + + std::string script_load(const StringView &script); + + // PUBSUB commands. + + long long publish(const StringView &channel, const StringView &message); + + // Transaction commands. + void watch(const StringView &key); + + template + void watch(Input first, Input last); + + template + void watch(std::initializer_list il) { + watch(il.begin(), il.end()); + } + + void unwatch(); + + // Stream commands. + + long long xack(const StringView &key, const StringView &group, const StringView &id); + + template + long long xack(const StringView &key, const StringView &group, Input first, Input last); + + template + long long xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, const StringView &id, Input first, Input last); + + template + std::string xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true); + + template + std::string xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il, + Output output) { + xclaim(key, group, consumer, min_idle_time, il.begin(), il.end(), output); + } + + long long xdel(const StringView &key, const StringView &id); + + template + long long xdel(const StringView &key, Input first, Input last); + + template + long long xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + void xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false); + + void xgroup_setid(const StringView &key, const StringView &group, const StringView &id); + + long long xgroup_destroy(const StringView &key, const StringView &group); + + long long xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer); + + long long xlen(const StringView &key); + + template + auto xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple; + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + Output output) { + xread(key, id, 0, output); + } + + template + auto xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, Input last, Output output) + -> typename std::enable_if::value>::type { + xread(first ,last, 0, output); + } + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xread(key, id, timeout, 0, output); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xread(first, last, timeout, 0, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + Output output) { + xreadgroup(group, consumer, key, id, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, 0, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, timeout, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xreadgroup(group, consumer, key, id, timeout, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, 0, false, output); + } + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output); + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output); + + long long xtrim(const StringView &key, long long count, bool approx = true); + +private: + template + friend class QueuedRedis; + + friend class RedisCluster; + + // For internal use. + explicit Redis(const GuardedConnectionSPtr &connection); + + template + ReplyUPtr _command(const StringView &cmd_name, const IndexSequence &, Args &&...args) { + return command(cmd_name, NthValue(std::forward(args)...)...); + } + + template + ReplyUPtr _command(Connection &connection, Cmd cmd, Args &&...args); + + template + ReplyUPtr _score_command(std::true_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(std::false_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(Cmd cmd, Args &&... args); + + // Pool Mode. + // Public constructors create a *Redis* instance with a pool. + // In this case, *_connection* is a null pointer, and is never used. + ConnectionPoolSPtr _pool; + + // Single Connection Mode. + // Private constructor creates a *Redis* instance with a single connection. + // This is used when we create Transaction, Pipeline and Subscriber. + // In this case, *_pool* is empty, and is never used. + GuardedConnectionSPtr _connection; +}; + +} + +} + +#include "redis.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.hpp b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.hpp new file mode 100644 index 000000000..c560ce111 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.hpp @@ -0,0 +1,1342 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_HPP +#define SEWENEW_REDISPLUSPLUS_REDIS_HPP + +#include "command.h" +#include "reply.h" +#include "utils.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +template +auto Redis::command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (_connection) { + // Single Connection Mode. + // TODO: In this case, should we reconnect? + auto &connection = _connection->connection(); + if (connection.broken()) { + throw Error("Connection is broken"); + } + + return _command(connection, cmd, std::forward(args)...); + } else { + assert(_pool); + + // Pool Mode, i.e. get connection from pool. + SafeConnection connection(*_pool); + + return _command(connection.connection(), cmd, std::forward(args)...); + } +} + +template +auto Redis::command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, ReplyUPtr>::type { + auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, std::forward(args)...); + connection.send(cmd_args); + }; + + return command(cmd, cmd_name, std::forward(args)...); +} + +template +auto Redis::command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type { + range_check("command", first, last); + + auto cmd = [](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +Result Redis::command(const StringView &cmd_name, Args &&...args) { + auto r = command(cmd_name, std::forward(args)...); + + assert(r); + + return reply::parse(*r); +} + +template +auto Redis::command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, void>::type { + auto r = _command(cmd_name, + MakeIndexSequence(), + std::forward(args)...); + + assert(r); + + reply::to_array(*r, LastValue(std::forward(args)...)); +} + +template +auto Redis::command(Input first, Input last) + -> typename std::enable_if::value, Result>::type { + auto r = command(first, last); + + assert(r); + + return reply::parse(*r); +} + +template +auto Redis::command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type { + auto r = command(first, last); + + assert(r); + + reply::to_array(*r, output); +} + +// KEY commands. + +template +long long Redis::del(Input first, Input last) { + range_check("DEL", first, last); + + auto reply = command(cmd::del_range, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::exists(Input first, Input last) { + range_check("EXISTS", first, last); + + auto reply = command(cmd::exists_range, first, last); + + return reply::parse(*reply); +} + +inline bool Redis::expire(const StringView &key, const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); +} + +inline bool Redis::expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); +} + +template +void Redis::keys(const StringView &pattern, Output output) { + auto reply = command(cmd::keys, pattern); + + reply::to_array(*reply, output); +} + +inline bool Redis::pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); +} + +inline bool Redis::pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); +} + +inline void Redis::restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + bool replace) { + return restore(key, val, ttl.count(), replace); +} + +template +long long Redis::scan(long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::scan, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::scan(long long cursor, + const StringView &pattern, + Output output) { + return scan(cursor, pattern, 10, output); +} + +template +inline long long Redis::scan(long long cursor, + long long count, + Output output) { + return scan(cursor, "*", count, output); +} + +template +inline long long Redis::scan(long long cursor, + Output output) { + return scan(cursor, "*", 10, output); +} + +template +long long Redis::touch(Input first, Input last) { + range_check("TOUCH", first, last); + + auto reply = command(cmd::touch_range, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::unlink(Input first, Input last) { + range_check("UNLINK", first, last); + + auto reply = command(cmd::unlink_range, first, last); + + return reply::parse(*reply); +} + +inline long long Redis::wait(long long numslaves, const std::chrono::milliseconds &timeout) { + return wait(numslaves, timeout.count()); +} + +// STRING commands. + +template +long long Redis::bitop(BitOp op, const StringView &destination, Input first, Input last) { + range_check("BITOP", first, last); + + auto reply = command(cmd::bitop_range, op, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::mget(Input first, Input last, Output output) { + range_check("MGET", first, last); + + auto reply = command(cmd::mget, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::mset(Input first, Input last) { + range_check("MSET", first, last); + + auto reply = command(cmd::mset, first, last); + + reply::parse(*reply); +} + +template +bool Redis::msetnx(Input first, Input last) { + range_check("MSETNX", first, last); + + auto reply = command(cmd::msetnx, first, last); + + return reply::parse(*reply); +} + +inline void Redis::psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); +} + +inline void Redis::setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + setex(key, ttl.count(), val); +} + +// LIST commands. + +template +OptionalStringPair Redis::blpop(Input first, Input last, long long timeout) { + range_check("BLPOP", first, last); + + auto reply = command(cmd::blpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair Redis::blpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return blpop(first, last, timeout.count()); +} + +template +OptionalStringPair Redis::brpop(Input first, Input last, long long timeout) { + range_check("BRPOP", first, last); + + auto reply = command(cmd::brpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair Redis::brpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return brpop(first, last, timeout.count()); +} + +inline OptionalString Redis::brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return brpoplpush(source, destination, timeout.count()); +} + +template +inline long long Redis::lpush(const StringView &key, Input first, Input last) { + range_check("LPUSH", first, last); + + auto reply = command(cmd::lpush_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::lrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = command(cmd::lrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline long long Redis::rpush(const StringView &key, Input first, Input last) { + range_check("RPUSH", first, last); + + auto reply = command(cmd::rpush_range, key, first, last); + + return reply::parse(*reply); +} + +// HASH commands. + +template +inline long long Redis::hdel(const StringView &key, Input first, Input last) { + range_check("HDEL", first, last); + + auto reply = command(cmd::hdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::hgetall(const StringView &key, Output output) { + auto reply = command(cmd::hgetall, key); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hkeys(const StringView &key, Output output) { + auto reply = command(cmd::hkeys, key); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hmget(const StringView &key, Input first, Input last, Output output) { + range_check("HMGET", first, last); + + auto reply = command(cmd::hmget, key, first, last); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hmset(const StringView &key, Input first, Input last) { + range_check("HMSET", first, last); + + auto reply = command(cmd::hmset, key, first, last); + + reply::parse(*reply); +} + +template +long long Redis::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::hscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return hscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return hscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + Output output) { + return hscan(key, cursor, "*", 10, output); +} + +template +auto Redis::hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, + long long>::type { + range_check("HSET", first, last); + + auto reply = command(cmd::hset_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::hvals(const StringView &key, Output output) { + auto reply = command(cmd::hvals, key); + + reply::to_array(*reply, output); +} + +// SET commands. + +template +long long Redis::sadd(const StringView &key, Input first, Input last) { + range_check("SADD", first, last); + + auto reply = command(cmd::sadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void Redis::sdiff(Input first, Input last, Output output) { + range_check("SDIFF", first, last); + + auto reply = command(cmd::sdiff, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sdiffstore(const StringView &destination, + Input first, + Input last) { + range_check("SDIFFSTORE", first, last); + + auto reply = command(cmd::sdiffstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::sinter(Input first, Input last, Output output) { + range_check("SINTER", first, last); + + auto reply = command(cmd::sinter, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sinterstore(const StringView &destination, + Input first, + Input last) { + range_check("SINTERSTORE", first, last); + + auto reply = command(cmd::sinterstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::smembers(const StringView &key, Output output) { + auto reply = command(cmd::smembers, key); + + reply::to_array(*reply, output); +} + +template +void Redis::spop(const StringView &key, long long count, Output output) { + auto reply = command(cmd::spop_range, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::srandmember(const StringView &key, long long count, Output output) { + auto reply = command(cmd::srandmember_range, key, count); + + reply::to_array(*reply, output); +} + +template +long long Redis::srem(const StringView &key, Input first, Input last) { + range_check("SREM", first, last); + + auto reply = command(cmd::srem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::sscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return sscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return sscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + Output output) { + return sscan(key, cursor, "*", 10, output); +} + +template +void Redis::sunion(Input first, Input last, Output output) { + range_check("SUNION", first, last); + + auto reply = command(cmd::sunion, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sunionstore(const StringView &destination, Input first, Input last) { + range_check("SUNIONSTORE", first, last); + + auto reply = command(cmd::sunionstore_range, destination, first, last); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +inline auto Redis::bzpopmax(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(key, timeout.count()); +} + +template +auto Redis::bzpopmax(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto Redis::bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(first, last, timeout.count()); +} + +inline auto Redis::bzpopmin(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(key, timeout.count()); +} + +template +auto Redis::bzpopmin(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto Redis::bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(first, last, timeout.count()); +} + +template +long long Redis::zadd(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + range_check("ZADD", first, last); + + auto reply = command(cmd::zadd_range, key, first, last, type, changed); + + return reply::parse(*reply); +} + +template +long long Redis::zcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zcount, key, interval); + + return reply::parse(*reply); +} + +template +long long Redis::zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZINTERSTORE", first, last); + + auto reply = command(cmd::zinterstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +template +long long Redis::zlexcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zlexcount, key, interval); + + return reply::parse(*reply); +} + +template +void Redis::zpopmax(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmax, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::zpopmin(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmin, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::zrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +void Redis::zrangebylex(const StringView &key, const Interval &interval, Output output) { + zrangebylex(key, interval, {}, output); +} + +template +void Redis::zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void Redis::zrangebyscore(const StringView &key, + const Interval &interval, + Output output) { + zrangebyscore(key, interval, {}, output); +} + +template +void Redis::zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrangebyscore, + key, + interval, + opts); + + reply::to_array(*reply, output); +} + +template +long long Redis::zrem(const StringView &key, Input first, Input last) { + range_check("ZREM", first, last); + + auto reply = command(cmd::zrem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::zremrangebylex(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebylex, key, interval); + + return reply::parse(*reply); +} + +template +long long Redis::zremrangebyscore(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebyscore, key, interval); + + return reply::parse(*reply); +} + +template +void Redis::zrevrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrevrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline void Redis::zrevrangebylex(const StringView &key, + const Interval &interval, + Output output) { + zrevrangebylex(key, interval, {}, output); +} + +template +void Redis::zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrevrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void Redis::zrevrangebyscore(const StringView &key, const Interval &interval, Output output) { + zrevrangebyscore(key, interval, {}, output); +} + +template +void Redis::zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrevrangebyscore, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +long long Redis::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::zscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return zscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return zscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + Output output) { + return zscan(key, cursor, "*", 10, output); +} + +template +long long Redis::zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZUNIONSTORE", first, last); + + auto reply = command(cmd::zunionstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +template +bool Redis::pfadd(const StringView &key, Input first, Input last) { + range_check("PFADD", first, last); + + auto reply = command(cmd::pfadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::pfcount(Input first, Input last) { + range_check("PFCOUNT", first, last); + + auto reply = command(cmd::pfcount_range, first, last); + + return reply::parse(*reply); +} + +template +void Redis::pfmerge(const StringView &destination, + Input first, + Input last) { + range_check("PFMERGE", first, last); + + auto reply = command(cmd::pfmerge_range, destination, first, last); + + reply::parse(*reply); +} + +// GEO commands. + +template +inline long long Redis::geoadd(const StringView &key, + Input first, + Input last) { + range_check("GEOADD", first, last); + + auto reply = command(cmd::geoadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void Redis::geohash(const StringView &key, Input first, Input last, Output output) { + range_check("GEOHASH", first, last); + + auto reply = command(cmd::geohash_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::geopos(const StringView &key, Input first, Input last, Output output) { + range_check("GEOPOS", first, last); + + auto reply = command(cmd::geopos_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +template +void Redis::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +// SCRIPTING commands. + +template +Result Redis::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + auto reply = command(cmd::eval, script, keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result Redis::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return eval(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void Redis::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + auto reply = command(cmd::eval, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void Redis::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + eval(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +template +Result Redis::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + auto reply = command(cmd::evalsha, script, + keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result Redis::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return evalsha(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void Redis::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + auto reply = command(cmd::evalsha, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void Redis::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + evalsha(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +template +void Redis::script_exists(Input first, Input last, Output output) { + range_check("SCRIPT EXISTS", first, last); + + auto reply = command(cmd::script_exists_range, first, last); + + reply::to_array(*reply, output); +} + +// Transaction commands. + +template +void Redis::watch(Input first, Input last) { + auto reply = command(cmd::watch_range, first, last); + + reply::parse(*reply); +} + +// Stream commands. + +template +long long Redis::xack(const StringView &key, const StringView &group, Input first, Input last) { + auto reply = command(cmd::xack_range, key, group, first, last); + + return reply::parse(*reply); +} + +template +std::string Redis::xadd(const StringView &key, const StringView &id, Input first, Input last) { + auto reply = command(cmd::xadd_range, key, id, first, last); + + return reply::parse(*reply); +} + +template +std::string Redis::xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + auto reply = command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + + return reply::parse(*reply); +} + +template +void Redis::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output) { + auto reply = command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + + reply::to_array(*reply, output); +} + +template +void Redis::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output) { + auto reply = command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + + reply::to_array(*reply, output); +} + +template +long long Redis::xdel(const StringView &key, Input first, Input last) { + auto reply = command(cmd::xdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +auto Redis::xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple { + auto reply = command(cmd::xpending, key, group); + + return reply::parse_xpending_reply(*reply, output); +} + +template +void Redis::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xpending_detail, key, group, start, end, count); + + reply::to_array(*reply, output); +} + +template +void Redis::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output) { + auto reply = command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + + reply::to_array(*reply, output); +} + +template +void Redis::xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output) { + auto reply = command(cmd::xrange, key, start, end); + + reply::to_array(*reply, output); +} + +template +void Redis::xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xrange_count, key, start, end, count); + + reply::to_array(*reply, output); +} + +template +void Redis::xread(const StringView &key, + const StringView &id, + long long count, + Output output) { + auto reply = command(cmd::xread, key, id, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_range, first, last, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + auto reply = command(cmd::xread_block, key, id, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_block_range, first, last, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output) { + auto reply = command(cmd::xreadgroup, group, consumer, key, id, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = command(cmd::xreadgroup_range, group, consumer, first, last, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) { + auto reply = command(cmd::xreadgroup_block, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = command(cmd::xreadgroup_block_range, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output) { + auto reply = command(cmd::xrevrange, key, end, start); + + reply::to_array(*reply, output); +} + +template +void Redis::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output) { + auto reply = command(cmd::xrevrange_count, key, end, start, count); + + reply::to_array(*reply, output); +} + +template +ReplyUPtr Redis::_command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + + auto reply = connection.recv(); + + return reply; +} + +template +inline ReplyUPtr Redis::_score_command(std::true_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., true); +} + +template +inline ReplyUPtr Redis::_score_command(std::false_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., false); +} + +template +inline ReplyUPtr Redis::_score_command(Cmd cmd, Args &&... args) { + return _score_command(typename IsKvPairIter::type(), + cmd, + std::forward(args)...); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_HPP diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.h new file mode 100644 index 000000000..63c6131c1 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.h @@ -0,0 +1,1439 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H +#define SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H + +#include +#include +#include +#include +#include "shards_pool.h" +#include "reply.h" +#include "command_options.h" +#include "utils.h" +#include "subscriber.h" +#include "pipeline.h" +#include "transaction.h" +#include "redis.h" +#include "connection.h" + +namespace sw { + +namespace redis { + +template +class QueuedRedis; + +using Transaction = QueuedRedis; + +using Pipeline = QueuedRedis; + +class RedisCluster { +public: + RedisCluster(const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}, + Role role = Role::MASTER) : _pool(pool_opts, connection_opts, role) {} + + // Construct RedisCluster with URI: + // "tcp://127.0.0.1" or "tcp://127.0.0.1:6379" + // Only need to specify one URI. + explicit RedisCluster(const std::string &uri); + + RedisCluster(const RedisCluster &) = delete; + RedisCluster& operator=(const RedisCluster &) = delete; + + RedisCluster(RedisCluster &&) = default; + RedisCluster& operator=(RedisCluster &&) = default; + + Redis redis(const StringView &hash_tag, bool new_connection = true); + + Pipeline pipeline(const StringView &hash_tag, bool new_connection = true); + + Transaction transaction(const StringView &hash_tag, bool piped = false, bool new_connection = true); + + Subscriber subscriber(); + + template + auto command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && !IsIter::type>::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && IsIter::type>::value, void>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if::value + || std::is_arithmetic::type>::value, Result>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Result>::type; + + template + auto command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type; + + // KEY commands. + + long long del(const StringView &key); + + template + long long del(Input first, Input last); + + template + long long del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + OptionalString dump(const StringView &key); + + long long exists(const StringView &key); + + template + long long exists(Input first, Input last); + + template + long long exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + bool expire(const StringView &key, long long timeout); + + bool expire(const StringView &key, const std::chrono::seconds &timeout); + + bool expireat(const StringView &key, long long timestamp); + + bool expireat(const StringView &key, + const std::chrono::time_point &tp); + + bool persist(const StringView &key); + + bool pexpire(const StringView &key, long long timeout); + + bool pexpire(const StringView &key, const std::chrono::milliseconds &timeout); + + bool pexpireat(const StringView &key, long long timestamp); + + bool pexpireat(const StringView &key, + const std::chrono::time_point &tp); + + long long pttl(const StringView &key); + + void rename(const StringView &key, const StringView &newkey); + + bool renamenx(const StringView &key, const StringView &newkey); + + void restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false); + + void restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false); + + // TODO: sort + + long long touch(const StringView &key); + + template + long long touch(Input first, Input last); + + template + long long touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + long long ttl(const StringView &key); + + std::string type(const StringView &key); + + long long unlink(const StringView &key); + + template + long long unlink(Input first, Input last); + + template + long long unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + // STRING commands. + + long long append(const StringView &key, const StringView &str); + + long long bitcount(const StringView &key, long long start = 0, long long end = -1); + + long long bitop(BitOp op, const StringView &destination, const StringView &key); + + template + long long bitop(BitOp op, const StringView &destination, Input first, Input last); + + template + long long bitop(BitOp op, const StringView &destination, std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + long long bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1); + + long long decr(const StringView &key); + + long long decrby(const StringView &key, long long decrement); + + OptionalString get(const StringView &key); + + long long getbit(const StringView &key, long long offset); + + std::string getrange(const StringView &key, long long start, long long end); + + OptionalString getset(const StringView &key, const StringView &val); + + long long incr(const StringView &key); + + long long incrby(const StringView &key, long long increment); + + double incrbyfloat(const StringView &key, double increment); + + template + void mget(Input first, Input last, Output output); + + template + void mget(std::initializer_list il, Output output) { + mget(il.begin(), il.end(), output); + } + + template + void mset(Input first, Input last); + + template + void mset(std::initializer_list il) { + mset(il.begin(), il.end()); + } + + template + bool msetnx(Input first, Input last); + + template + bool msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + void psetex(const StringView &key, + long long ttl, + const StringView &val); + + void psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val); + + bool set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS); + + void setex(const StringView &key, + long long ttl, + const StringView &val); + + void setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val); + + bool setnx(const StringView &key, const StringView &val); + + long long setrange(const StringView &key, long long offset, const StringView &val); + + long long strlen(const StringView &key); + + // LIST commands. + + OptionalStringPair blpop(const StringView &key, long long timeout); + + OptionalStringPair blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(Input first, Input last, long long timeout); + + template + OptionalStringPair blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + OptionalStringPair brpop(const StringView &key, long long timeout); + + OptionalStringPair brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(Input first, Input last, long long timeout); + + template + OptionalStringPair brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + long long timeout); + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + OptionalString lindex(const StringView &key, long long index); + + long long linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + + long long llen(const StringView &key); + + OptionalString lpop(const StringView &key); + + long long lpush(const StringView &key, const StringView &val); + + template + long long lpush(const StringView &key, Input first, Input last); + + template + long long lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + long long lpushx(const StringView &key, const StringView &val); + + template + void lrange(const StringView &key, long long start, long long stop, Output output); + + long long lrem(const StringView &key, long long count, const StringView &val); + + void lset(const StringView &key, long long index, const StringView &val); + + void ltrim(const StringView &key, long long start, long long stop); + + OptionalString rpop(const StringView &key); + + OptionalString rpoplpush(const StringView &source, const StringView &destination); + + long long rpush(const StringView &key, const StringView &val); + + template + long long rpush(const StringView &key, Input first, Input last); + + template + long long rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + long long rpushx(const StringView &key, const StringView &val); + + // HASH commands. + + long long hdel(const StringView &key, const StringView &field); + + template + long long hdel(const StringView &key, Input first, Input last); + + template + long long hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + bool hexists(const StringView &key, const StringView &field); + + OptionalString hget(const StringView &key, const StringView &field); + + template + void hgetall(const StringView &key, Output output); + + long long hincrby(const StringView &key, const StringView &field, long long increment); + + double hincrbyfloat(const StringView &key, const StringView &field, double increment); + + template + void hkeys(const StringView &key, Output output); + + long long hlen(const StringView &key); + + template + void hmget(const StringView &key, Input first, Input last, Output output); + + template + void hmget(const StringView &key, std::initializer_list il, Output output) { + hmget(key, il.begin(), il.end(), output); + } + + template + void hmset(const StringView &key, Input first, Input last); + + template + void hmset(const StringView &key, std::initializer_list il) { + hmset(key, il.begin(), il.end()); + } + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + Output output); + + bool hset(const StringView &key, const StringView &field, const StringView &val); + + bool hset(const StringView &key, const std::pair &item); + + template + auto hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, long long>::type; + + template + long long hset(const StringView &key, std::initializer_list il) { + return hset(key, il.begin(), il.end()); + } + + bool hsetnx(const StringView &key, const StringView &field, const StringView &val); + + bool hsetnx(const StringView &key, const std::pair &item); + + long long hstrlen(const StringView &key, const StringView &field); + + template + void hvals(const StringView &key, Output output); + + // SET commands. + + long long sadd(const StringView &key, const StringView &member); + + template + long long sadd(const StringView &key, Input first, Input last); + + template + long long sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + long long scard(const StringView &key); + + template + void sdiff(Input first, Input last, Output output); + + template + void sdiff(std::initializer_list il, Output output) { + sdiff(il.begin(), il.end(), output); + } + + long long sdiffstore(const StringView &destination, const StringView &key); + + template + long long sdiffstore(const StringView &destination, + Input first, + Input last); + + template + long long sdiffstore(const StringView &destination, + std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + void sinter(Input first, Input last, Output output); + + template + void sinter(std::initializer_list il, Output output) { + sinter(il.begin(), il.end(), output); + } + + long long sinterstore(const StringView &destination, const StringView &key); + + template + long long sinterstore(const StringView &destination, + Input first, + Input last); + + template + long long sinterstore(const StringView &destination, + std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + bool sismember(const StringView &key, const StringView &member); + + template + void smembers(const StringView &key, Output output); + + bool smove(const StringView &source, + const StringView &destination, + const StringView &member); + + OptionalString spop(const StringView &key); + + template + void spop(const StringView &key, long long count, Output output); + + OptionalString srandmember(const StringView &key); + + template + void srandmember(const StringView &key, long long count, Output output); + + long long srem(const StringView &key, const StringView &member); + + template + long long srem(const StringView &key, Input first, Input last); + + template + long long srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + Output output); + + template + void sunion(Input first, Input last, Output output); + + template + void sunion(std::initializer_list il, Output output) { + sunion(il.begin(), il.end(), output); + } + + long long sunionstore(const StringView &destination, const StringView &key); + + template + long long sunionstore(const StringView &destination, Input first, Input last); + + template + long long sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + auto bzpopmax(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + auto bzpopmin(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + long long zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + long long zcard(const StringView &key); + + template + long long zcount(const StringView &key, const Interval &interval); + + double zincrby(const StringView &key, double increment, const StringView &member); + + long long zinterstore(const StringView &destination, const StringView &key, double weight); + + template + long long zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + long long zlexcount(const StringView &key, const Interval &interval); + + Optional> zpopmax(const StringView &key); + + template + void zpopmax(const StringView &key, long long count, Output output); + + Optional> zpopmin(const StringView &key); + + template + void zpopmin(const StringView &key, long long count, Output output); + + // If *output* is an iterator of a container of string, + // we send *ZRANGE key start stop* command. + // If it's an iterator of a container of pair, + // we send *ZRANGE key start stop WITHSCORES* command. + // + // The following code sends *ZRANGE* without the *WITHSCORES* option: + // + // vector result; + // redis.zrange("key", 0, -1, back_inserter(result)); + // + // On the other hand, the following code sends command with *WITHSCORES* option: + // + // unordered_map with_score; + // redis.zrange("key", 0, -1, inserter(with_score, with_score.end())); + // + // This also applies to other commands with the *WITHSCORES* option, + // e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, *ZREVRANGEBYSCORE*. + template + void zrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrank(const StringView &key, const StringView &member); + + long long zrem(const StringView &key, const StringView &member); + + template + long long zrem(const StringView &key, Input first, Input last); + + template + long long zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + long long zremrangebylex(const StringView &key, const Interval &interval); + + long long zremrangebyrank(const StringView &key, long long start, long long stop); + + template + long long zremrangebyscore(const StringView &key, const Interval &interval); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrevrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrevrank(const StringView &key, const StringView &member); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + Output output); + + OptionalDouble zscore(const StringView &key, const StringView &member); + + long long zunionstore(const StringView &destination, const StringView &key, double weight); + + template + long long zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + bool pfadd(const StringView &key, const StringView &element); + + template + bool pfadd(const StringView &key, Input first, Input last); + + template + bool pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + long long pfcount(const StringView &key); + + template + long long pfcount(Input first, Input last); + + template + long long pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + void pfmerge(const StringView &destination, const StringView &key); + + template + void pfmerge(const StringView &destination, Input first, Input last); + + template + void pfmerge(const StringView &destination, std::initializer_list il) { + pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + long long geoadd(const StringView &key, + const std::tuple &member); + + template + long long geoadd(const StringView &key, + Input first, + Input last); + + template + long long geoadd(const StringView &key, + std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + OptionalDouble geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M); + + OptionalString geohash(const StringView &key, const StringView &member); + + template + void geohash(const StringView &key, Input first, Input last, Output output); + + template + void geohash(const StringView &key, std::initializer_list il, Output output) { + geohash(key, il.begin(), il.end(), output); + } + + Optional> geopos(const StringView &key, const StringView &member); + + template + void geopos(const StringView &key, Input first, Input last, Output output); + + template + void geopos(const StringView &key, std::initializer_list il, Output output) { + geopos(key, il.begin(), il.end(), output); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + OptionalLongLong georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // If *output* is an iterator of a container of string, we send *GEORADIUS* command + // without any options and only get the members in the specified geo range. + // If *output* is an iterator of a container of a tuple, the type of the tuple decides + // options we send with the *GEORADIUS* command. If the tuple has an element of type + // double, we send the *WITHDIST* option. If it has an element of type string, we send + // the *WITHHASH* option. If it has an element of type pair, we send + // the *WITHCOORD* option. For example: + // + // The following code only gets the members in range, i.e. without any option. + // + // vector members; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(members)) + // + // The following code sends the command with *WITHDIST* option. + // + // vector> with_dist; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist)) + // + // The following code sends the command with *WITHDIST* and *WITHHASH* options. + // + // vector> with_dist_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_hash)) + // + // The following code sends the command with *WITHDIST*, *WITHCOORD* and *WITHHASH* options. + // + // vector, string>> with_dist_coord_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_coord_hash)) + // + // This also applies to *GEORADIUSBYMEMBER*. + template + void georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + OptionalLongLong georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // See comments on *GEORADIUS*. + template + void georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + // SCRIPTING commands. + + template + Result eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + Result evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + // PUBSUB commands. + + long long publish(const StringView &channel, const StringView &message); + + // Stream commands. + + long long xack(const StringView &key, const StringView &group, const StringView &id); + + template + long long xack(const StringView &key, const StringView &group, Input first, Input last); + + template + long long xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, const StringView &id, Input first, Input last); + + template + std::string xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true); + + template + std::string xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il, + Output output) { + xclaim(key, group, consumer, min_idle_time, il.begin(), il.end(), output); + } + + long long xdel(const StringView &key, const StringView &id); + + template + long long xdel(const StringView &key, Input first, Input last); + + template + long long xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + void xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false); + + void xgroup_setid(const StringView &key, const StringView &group, const StringView &id); + + long long xgroup_destroy(const StringView &key, const StringView &group); + + long long xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer); + + long long xlen(const StringView &key); + + template + auto xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple; + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + Output output) { + xread(key, id, 0, output); + } + + template + auto xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, Input last, Output output) + -> typename std::enable_if::value>::type { + xread(first, last, 0, output); + } + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xread(key, id, timeout, 0, output); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xread(first, last, timeout, 0, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + Output output) { + xreadgroup(group, consumer, key, id, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, 0, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + return xreadgroup(group, consumer, key, id, timeout, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + return xreadgroup(group, consumer, key, id, timeout, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, 0, false, output); + } + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output); + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output); + + long long xtrim(const StringView &key, long long count, bool approx = true); + +private: + class Command { + public: + explicit Command(const StringView &cmd_name) : _cmd_name(cmd_name) {} + + template + void operator()(Connection &connection, Args &&...args) const { + CmdArgs cmd_args; + cmd_args.append(_cmd_name, std::forward(args)...); + connection.send(cmd_args); + } + + private: + StringView _cmd_name; + }; + + template + auto _generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, + ReplyUPtr>::type; + + template + auto _generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type; + + template + ReplyUPtr _command(Cmd cmd, Connection &connection, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, const StringView &key, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, std::true_type, const StringView &key, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, std::false_type, Input &&first, Args &&...args); + + template + ReplyUPtr _command(const StringView &cmd_name, const IndexSequence &, Args &&...args) { + return command(cmd_name, NthValue(std::forward(args)...)...); + } + + template + ReplyUPtr _range_command(Cmd cmd, std::true_type, Input input, Args &&...args); + + template + ReplyUPtr _range_command(Cmd cmd, std::false_type, Input input, Args &&...args); + + void _asking(Connection &connection); + + template + ReplyUPtr _score_command(std::true_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(std::false_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(Cmd cmd, Args &&... args); + + ShardsPool _pool; +}; + +} + +} + +#include "redis_cluster.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.hpp b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.hpp new file mode 100644 index 000000000..2153d181b --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.hpp @@ -0,0 +1,1403 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP +#define SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP + +#include +#include "command.h" +#include "reply.h" +#include "utils.h" +#include "errors.h" +#include "shards_pool.h" + +namespace sw { + +namespace redis { + +template +auto RedisCluster::command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type { + return _command(cmd, + std::is_convertible::type, StringView>(), + std::forward(key), + std::forward(args)...); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && !IsIter::type>::value, ReplyUPtr>::type { + auto cmd = Command(cmd_name); + + return _generic_command(cmd, std::forward(key), std::forward(args)...); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if::value + || std::is_arithmetic::type>::value, Result>::type { + auto r = command(cmd_name, std::forward(key), std::forward(args)...); + + assert(r); + + return reply::parse(*r); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && IsIter::type>::value, void>::type { + auto r = _command(cmd_name, + MakeIndexSequence(), + std::forward(key), + std::forward(args)...); + + assert(r); + + reply::to_array(*r, LastValue(std::forward(args)...)); +} + +template +auto RedisCluster::command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (first == last || std::next(first) == last) { + throw Error("command: invalid range"); + } + + const auto &key = *first; + ++first; + + auto cmd = [&key](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + cmd_args.append(key); + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +auto RedisCluster::command(Input first, Input last) + -> typename std::enable_if::value, Result>::type { + auto r = command(first, last); + + assert(r); + + return reply::parse(*r); +} + +template +auto RedisCluster::command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type { + auto r = command(first, last); + + assert(r); + + reply::to_array(*r, output); +} + +// KEY commands. + +template +long long RedisCluster::del(Input first, Input last) { + range_check("DEL", first, last); + + auto reply = command(cmd::del_range, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::exists(Input first, Input last) { + range_check("EXISTS", first, last); + + auto reply = command(cmd::exists_range, first, last); + + return reply::parse(*reply); +} + +inline bool RedisCluster::expire(const StringView &key, const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); +} + +inline bool RedisCluster::expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); +} + +inline bool RedisCluster::pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); +} + +inline bool RedisCluster::pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); +} + +inline void RedisCluster::restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + bool replace) { + return restore(key, val, ttl.count(), replace); +} + +template +long long RedisCluster::touch(Input first, Input last) { + range_check("TOUCH", first, last); + + auto reply = command(cmd::touch_range, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::unlink(Input first, Input last) { + range_check("UNLINK", first, last); + + auto reply = command(cmd::unlink_range, first, last); + + return reply::parse(*reply); +} + +// STRING commands. + +template +long long RedisCluster::bitop(BitOp op, const StringView &destination, Input first, Input last) { + range_check("BITOP", first, last); + + auto reply = _command(cmd::bitop_range, destination, op, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::mget(Input first, Input last, Output output) { + range_check("MGET", first, last); + + auto reply = command(cmd::mget, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::mset(Input first, Input last) { + range_check("MSET", first, last); + + auto reply = command(cmd::mset, first, last); + + reply::parse(*reply); +} + +template +bool RedisCluster::msetnx(Input first, Input last) { + range_check("MSETNX", first, last); + + auto reply = command(cmd::msetnx, first, last); + + return reply::parse(*reply); +} + +inline void RedisCluster::psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); +} + +inline void RedisCluster::setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + setex(key, ttl.count(), val); +} + +// LIST commands. + +template +OptionalStringPair RedisCluster::blpop(Input first, Input last, long long timeout) { + range_check("BLPOP", first, last); + + auto reply = command(cmd::blpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair RedisCluster::blpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return blpop(first, last, timeout.count()); +} + +template +OptionalStringPair RedisCluster::brpop(Input first, Input last, long long timeout) { + range_check("BRPOP", first, last); + + auto reply = command(cmd::brpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair RedisCluster::brpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return brpop(first, last, timeout.count()); +} + +inline OptionalString RedisCluster::brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return brpoplpush(source, destination, timeout.count()); +} + +template +inline long long RedisCluster::lpush(const StringView &key, Input first, Input last) { + range_check("LPUSH", first, last); + + auto reply = command(cmd::lpush_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::lrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = command(cmd::lrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline long long RedisCluster::rpush(const StringView &key, Input first, Input last) { + range_check("RPUSH", first, last); + + auto reply = command(cmd::rpush_range, key, first, last); + + return reply::parse(*reply); +} + +// HASH commands. + +template +inline long long RedisCluster::hdel(const StringView &key, Input first, Input last) { + range_check("HDEL", first, last); + + auto reply = command(cmd::hdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::hgetall(const StringView &key, Output output) { + auto reply = command(cmd::hgetall, key); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hkeys(const StringView &key, Output output) { + auto reply = command(cmd::hkeys, key); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hmget(const StringView &key, Input first, Input last, Output output) { + range_check("HMGET", first, last); + + auto reply = command(cmd::hmget, key, first, last); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hmset(const StringView &key, Input first, Input last) { + range_check("HMSET", first, last); + + auto reply = command(cmd::hmset, key, first, last); + + reply::parse(*reply); +} + +template +long long RedisCluster::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::hscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return hscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return hscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + Output output) { + return hscan(key, cursor, "*", 10, output); +} + +template +auto RedisCluster::hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, + long long>::type { + range_check("HSET", first, last); + + auto reply = command(cmd::hset_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::hvals(const StringView &key, Output output) { + auto reply = command(cmd::hvals, key); + + reply::to_array(*reply, output); +} + +// SET commands. + +template +long long RedisCluster::sadd(const StringView &key, Input first, Input last) { + range_check("SADD", first, last); + + auto reply = command(cmd::sadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::sdiff(Input first, Input last, Output output) { + range_check("SDIFF", first, last); + + auto reply = command(cmd::sdiff, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sdiffstore(const StringView &destination, + Input first, + Input last) { + range_check("SDIFFSTORE", first, last); + + auto reply = command(cmd::sdiffstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::sinter(Input first, Input last, Output output) { + range_check("SINTER", first, last); + + auto reply = command(cmd::sinter, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sinterstore(const StringView &destination, + Input first, + Input last) { + range_check("SINTERSTORE", first, last); + + auto reply = command(cmd::sinterstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::smembers(const StringView &key, Output output) { + auto reply = command(cmd::smembers, key); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::spop(const StringView &key, long long count, Output output) { + auto reply = command(cmd::spop_range, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::srandmember(const StringView &key, long long count, Output output) { + auto reply = command(cmd::srandmember_range, key, count); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::srem(const StringView &key, Input first, Input last) { + range_check("SREM", first, last); + + auto reply = command(cmd::srem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::sscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return sscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return sscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + Output output) { + return sscan(key, cursor, "*", 10, output); +} + +template +void RedisCluster::sunion(Input first, Input last, Output output) { + range_check("SUNION", first, last); + + auto reply = command(cmd::sunion, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sunionstore(const StringView &destination, Input first, Input last) { + range_check("SUNIONSTORE", first, last); + + auto reply = command(cmd::sunionstore_range, destination, first, last); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +inline auto RedisCluster::bzpopmax(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(key, timeout.count()); +} + +template +auto RedisCluster::bzpopmax(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto RedisCluster::bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(first, last, timeout.count()); +} + +inline auto RedisCluster::bzpopmin(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(key, timeout.count()); +} + +template +auto RedisCluster::bzpopmin(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto RedisCluster::bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(first, last, timeout.count()); +} + +template +long long RedisCluster::zadd(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + range_check("ZADD", first, last); + + auto reply = command(cmd::zadd_range, key, first, last, type, changed); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zcount, key, interval); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZINTERSTORE", first, last); + + auto reply = command(cmd::zinterstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zlexcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zlexcount, key, interval); + + return reply::parse(*reply); +} + +template +void RedisCluster::zpopmax(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmax, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zpopmin(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmin, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrangebylex(const StringView &key, const Interval &interval, Output output) { + zrangebylex(key, interval, {}, output); +} + +template +void RedisCluster::zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrangebyscore(const StringView &key, + const Interval &interval, + Output output) { + zrangebyscore(key, interval, {}, output); +} + +template +void RedisCluster::zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrangebyscore, + key, + interval, + opts); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::zrem(const StringView &key, Input first, Input last) { + range_check("ZREM", first, last); + + auto reply = command(cmd::zrem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zremrangebylex(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebylex, key, interval); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zremrangebyscore(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebyscore, key, interval); + + return reply::parse(*reply); +} + +template +void RedisCluster::zrevrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrevrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::zrevrangebylex(const StringView &key, + const Interval &interval, + Output output) { + zrevrangebylex(key, interval, {}, output); +} + +template +void RedisCluster::zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrevrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrevrangebyscore(const StringView &key, const Interval &interval, Output output) { + zrevrangebyscore(key, interval, {}, output); +} + +template +void RedisCluster::zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrevrangebyscore, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::zscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return zscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return zscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + Output output) { + return zscan(key, cursor, "*", 10, output); +} + +template +long long RedisCluster::zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZUNIONSTORE", first, last); + + auto reply = command(cmd::zunionstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +template +bool RedisCluster::pfadd(const StringView &key, Input first, Input last) { + range_check("PFADD", first, last); + + auto reply = command(cmd::pfadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::pfcount(Input first, Input last) { + range_check("PFCOUNT", first, last); + + auto reply = command(cmd::pfcount_range, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::pfmerge(const StringView &destination, + Input first, + Input last) { + range_check("PFMERGE", first, last); + + auto reply = command(cmd::pfmerge_range, destination, first, last); + + reply::parse(*reply); +} + +// GEO commands. + +template +inline long long RedisCluster::geoadd(const StringView &key, + Input first, + Input last) { + range_check("GEOADD", first, last); + + auto reply = command(cmd::geoadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::geohash(const StringView &key, Input first, Input last, Output output) { + range_check("GEOHASH", first, last); + + auto reply = command(cmd::geohash_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::geopos(const StringView &key, Input first, Input last, Output output) { + range_check("GEOPOS", first, last); + + auto reply = command(cmd::geopos_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +// SCRIPTING commands. + +template +Result RedisCluster::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::eval, *keys_first, script, keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result RedisCluster::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return eval(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void RedisCluster::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::eval, + *keys_first, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + eval(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +template +Result RedisCluster::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::evalsha, *keys_first, script, + keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result RedisCluster::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return evalsha(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void RedisCluster::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::evalsha, + *keys_first, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + evalsha(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +// Stream commands. + +template +long long RedisCluster::xack(const StringView &key, + const StringView &group, + Input first, + Input last) { + auto reply = command(cmd::xack_range, key, group, first, last); + + return reply::parse(*reply); +} + +template +std::string RedisCluster::xadd(const StringView &key, + const StringView &id, + Input first, + Input last) { + auto reply = command(cmd::xadd_range, key, id, first, last); + + return reply::parse(*reply); +} + +template +std::string RedisCluster::xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + auto reply = command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + + return reply::parse(*reply); +} + +template +void RedisCluster::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output) { + auto reply = command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output) { + auto reply = command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::xdel(const StringView &key, Input first, Input last) { + auto reply = command(cmd::xdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +auto RedisCluster::xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple { + auto reply = command(cmd::xpending, key, group); + + return reply::parse_xpending_reply(*reply, output); +} + +template +void RedisCluster::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xpending_detail, key, group, start, end, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output) { + auto reply = command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output) { + auto reply = command(cmd::xrange, key, start, end); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xrange_count, key, start, end, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xread(const StringView &key, + const StringView &id, + long long count, + Output output) { + auto reply = command(cmd::xread, key, id, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_range, first, last, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + auto reply = command(cmd::xread_block, key, id, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_block_range, first, last, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output) { + auto reply = _command(cmd::xreadgroup, key, group, consumer, key, id, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = _command(cmd::xreadgroup_range, + first->first, + group, + consumer, + first, + last, + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) { + auto reply = _command(cmd::xreadgroup_block, + key, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = _command(cmd::xreadgroup_block_range, + first->first, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output) { + auto reply = command(cmd::xrevrange, key, end, start); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output) { + auto reply = command(cmd::xrevrange_count, key, end, start, count); + + reply::to_array(*reply, output); +} + +template +auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, + ReplyUPtr>::type { + return command(cmd, std::forward(key), std::forward(args)...); +} + +template +auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type { + auto k = std::to_string(std::forward(key)); + return command(cmd, k, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, std::true_type, const StringView &key, Args &&...args) { + return _command(cmd, key, key, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, std::false_type, Input &&first, Args &&...args) { + return _range_command(cmd, + std::is_convertible< + typename std::decay< + decltype(*std::declval())>::type, StringView>(), + std::forward(first), + std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::true_type, Input input, Args &&...args) { + return _command(cmd, *input, input, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::false_type, Input input, Args &&...args) { + return _command(cmd, std::get<0>(*input), input, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, Connection &connection, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + + return connection.recv(); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, const StringView &key, Args &&...args) { + for (auto idx = 0; idx < 2; ++idx) { + try { + auto pool = _pool.fetch(key); + assert(pool); + SafeConnection safe_connection(*pool); + + return _command(cmd, safe_connection.connection(), std::forward(args)...); + } catch (const IoError &err) { + // When master is down, one of its replicas will be promoted to be the new master. + // If we try to send command to the old master, we'll get an *IoError*. + // In this case, we need to update the slots mapping. + _pool.update(); + } catch (const ClosedError &err) { + // Node might be removed. + // 1. Get up-to-date slot mapping to check if the node still exists. + _pool.update(); + + // TODO: + // 2. If it's NOT exist, update slot mapping, and retry. + // 3. If it's still exist, that means the node is down, NOT removed, throw exception. + } catch (const MovedError &err) { + // Slot mapping has been changed, update it and try again. + _pool.update(); + } catch (const AskError &err) { + auto pool = _pool.fetch(err.node()); + assert(pool); + SafeConnection safe_connection(*pool); + auto &connection = safe_connection.connection(); + + // 1. send ASKING command. + _asking(connection); + + // 2. resend last command. + try { + return _command(cmd, connection, std::forward(args)...); + } catch (const MovedError &err) { + throw Error("Slot migrating... ASKING node hasn't been set to IMPORTING state"); + } + } // For other exceptions, just throw it. + } + + // Possible failures: + // 1. Source node has already run 'CLUSTER SETSLOT xxx NODE xxx', + // while the destination node has NOT run it. + // In this case, client will be redirected by both nodes with MovedError. + // 2. Other failures... + throw Error("Failed to send command with key: " + std::string(key.data(), key.size())); +} + +template +inline ReplyUPtr RedisCluster::_score_command(std::true_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., true); +} + +template +inline ReplyUPtr RedisCluster::_score_command(std::false_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., false); +} + +template +inline ReplyUPtr RedisCluster::_score_command(Cmd cmd, Args &&... args) { + return _score_command(typename IsKvPairIter::type(), + cmd, + std::forward(args)...); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/reply.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/reply.h new file mode 100644 index 000000000..d89174329 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/reply.h @@ -0,0 +1,435 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REPLY_H +#define SEWENEW_REDISPLUSPLUS_REPLY_H + +#include +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +struct ReplyDeleter { + void operator()(redisReply *reply) const { + if (reply != nullptr) { + freeReplyObject(reply); + } + } +}; + +using ReplyUPtr = std::unique_ptr; + +namespace reply { + +template +struct ParseTag {}; + +template +inline T parse(redisReply &reply) { + return parse(ParseTag(), reply); +} + +template +T parse_leniently(redisReply &reply); + +void parse(ParseTag, redisReply &reply); + +std::string parse(ParseTag, redisReply &reply); + +long long parse(ParseTag, redisReply &reply); + +double parse(ParseTag, redisReply &reply); + +bool parse(ParseTag, redisReply &reply); + +template +Optional parse(ParseTag>, redisReply &reply); + +template +std::pair parse(ParseTag>, redisReply &reply); + +template +std::tuple parse(ParseTag>, redisReply &reply); + +#ifdef REDIS_PLUS_PLUS_HAS_VARIANT + +inline Monostate parse(ParseTag, redisReply &) { + // Just ignore the reply + return {}; +} + +template +Variant parse(ParseTag>, redisReply &reply); + +#endif + +template ::value, int>::type = 0> +T parse(ParseTag, redisReply &reply); + +template ::value, int>::type = 0> +T parse(ParseTag, redisReply &reply); + +template +long long parse_scan_reply(redisReply &reply, Output output); + +inline bool is_error(redisReply &reply) { + return reply.type == REDIS_REPLY_ERROR; +} + +inline bool is_nil(redisReply &reply) { + return reply.type == REDIS_REPLY_NIL; +} + +inline bool is_string(redisReply &reply) { + return reply.type == REDIS_REPLY_STRING; +} + +inline bool is_status(redisReply &reply) { + return reply.type == REDIS_REPLY_STATUS; +} + +inline bool is_integer(redisReply &reply) { + return reply.type == REDIS_REPLY_INTEGER; +} + +inline bool is_array(redisReply &reply) { + return reply.type == REDIS_REPLY_ARRAY; +} + +std::string to_status(redisReply &reply); + +template +void to_array(redisReply &reply, Output output); + +// Rewrite set reply to bool type +void rewrite_set_reply(redisReply &reply); + +// Some command might return an empty array reply as a nil reply, +// e.g. georadius, zpopmin, zpopmax. In this case, we rewrite the +// reply to a nil reply. +void rewrite_empty_array_reply(redisReply &reply); + +template +auto parse_xpending_reply(redisReply &reply, Output output) + -> std::tuple; + +} + +// Inline implementations. + +namespace reply { + +namespace detail { + +template +void to_array(redisReply &reply, Output output) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.element == nullptr) { + // Empty array. + return; + } + + for (std::size_t idx = 0; idx != reply.elements; ++idx) { + auto *sub_reply = reply.element[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null array element reply"); + } + + *output = parse::type>(*sub_reply); + + ++output; + } +} + +bool is_flat_array(redisReply &reply); + +template +void to_flat_array(redisReply &reply, Output output) { + if (reply.element == nullptr) { + // Empty array. + return; + } + + if (reply.elements % 2 != 0) { + throw ProtoError("Not string pair array reply"); + } + + for (std::size_t idx = 0; idx != reply.elements; idx += 2) { + auto *key_reply = reply.element[idx]; + auto *val_reply = reply.element[idx + 1]; + if (key_reply == nullptr || val_reply == nullptr) { + throw ProtoError("Null string array reply"); + } + + using Pair = typename IterType::type; + using FirstType = typename std::decay::type; + using SecondType = typename std::decay::type; + *output = std::make_pair(parse(*key_reply), + parse(*val_reply)); + + ++output; + } +} + +template +void to_array(std::true_type, redisReply &reply, Output output) { + if (is_flat_array(reply)) { + to_flat_array(reply, output); + } else { + to_array(reply, output); + } +} + +template +void to_array(std::false_type, redisReply &reply, Output output) { + to_array(reply, output); +} + +template +std::tuple parse_tuple(redisReply **reply, std::size_t idx) { + assert(reply != nullptr); + + auto *sub_reply = reply[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null reply"); + } + + return std::make_tuple(parse(*sub_reply)); +} + +template +auto parse_tuple(redisReply **reply, std::size_t idx) -> + typename std::enable_if>::type { + assert(reply != nullptr); + + return std::tuple_cat(parse_tuple(reply, idx), + parse_tuple(reply, idx + 1)); +} + +#ifdef REDIS_PLUS_PLUS_HAS_VARIANT + +template +Variant parse_variant(redisReply &reply) { + return parse(reply); +} + +template +auto parse_variant(redisReply &reply) -> + typename std::enable_if>::type { + auto return_var = [](auto &&arg) { + return Variant(std::move(arg)); + }; + + try { + return std::visit(return_var, parse_variant(reply)); + } catch (const ProtoError &) { + return std::visit(return_var, parse_variant(reply)); + } +} + +#endif + +} + +template +T parse_leniently(redisReply &reply) { + if (is_array(reply) && reply.elements == 1) { + if (reply.element == nullptr) { + throw ProtoError("null array reply"); + } + + auto *ele = reply.element[0]; + if (ele != nullptr) { + return parse(*ele); + } // else fall through + } + + return parse(reply); +} + +template +Optional parse(ParseTag>, redisReply &reply) { + if (reply::is_nil(reply)) { + // Because of a GCC bug, we cannot return {} for -std=c++17 + // Refer to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86465 +#if defined REDIS_PLUS_PLUS_HAS_OPTIONAL + return std::nullopt; +#else + return {}; +#endif + } + + return Optional(parse(reply)); +} + +template +std::pair parse(ParseTag>, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.elements != 2) { + throw ProtoError("NOT key-value PAIR reply"); + } + + if (reply.element == nullptr) { + throw ProtoError("Null PAIR reply"); + } + + auto *first = reply.element[0]; + auto *second = reply.element[1]; + if (first == nullptr || second == nullptr) { + throw ProtoError("Null pair reply"); + } + + return std::make_pair(parse::type>(*first), + parse::type>(*second)); +} + +template +std::tuple parse(ParseTag>, redisReply &reply) { + constexpr auto size = sizeof...(Args); + + static_assert(size > 0, "DO NOT support parsing tuple with 0 element"); + + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.elements != size) { + throw ProtoError("Expect tuple reply with " + std::to_string(size) + "elements"); + } + + if (reply.element == nullptr) { + throw ProtoError("Null TUPLE reply"); + } + + return detail::parse_tuple(reply.element, 0); +} + +#ifdef REDIS_PLUS_PLUS_HAS_VARIANT + +template +Variant parse(ParseTag>, redisReply &reply) { + return detail::parse_variant(reply); +} + +#endif + +template ::value, int>::type> +T parse(ParseTag, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + T container; + + to_array(reply, std::back_inserter(container)); + + return container; +} + +template ::value, int>::type> +T parse(ParseTag, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + T container; + + to_array(reply, std::inserter(container, container.end())); + + return container; +} + +template +long long parse_scan_reply(redisReply &reply, Output output) { + if (reply.elements != 2 || reply.element == nullptr) { + throw ProtoError("Invalid scan reply"); + } + + auto *cursor_reply = reply.element[0]; + auto *data_reply = reply.element[1]; + if (cursor_reply == nullptr || data_reply == nullptr) { + throw ProtoError("Invalid cursor reply or data reply"); + } + + auto cursor_str = reply::parse(*cursor_reply); + long long new_cursor = 0; + try { + new_cursor = std::stoll(cursor_str); + } catch (const std::exception &e) { + throw ProtoError("Invalid cursor reply: " + cursor_str); + } + + reply::to_array(*data_reply, output); + + return new_cursor; +} + +template +void to_array(redisReply &reply, Output output) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + detail::to_array(typename IsKvPairIter::type(), reply, output); +} + +template +auto parse_xpending_reply(redisReply &reply, Output output) + -> std::tuple { + if (!is_array(reply) || reply.elements != 4) { + throw ProtoError("expect array reply with 4 elements"); + } + + for (std::size_t idx = 0; idx != reply.elements; ++idx) { + if (reply.element[idx] == nullptr) { + throw ProtoError("null array reply"); + } + } + + auto num = parse(*(reply.element[0])); + auto start = parse(*(reply.element[1])); + auto end = parse(*(reply.element[2])); + + auto &entry_reply = *(reply.element[3]); + if (!is_nil(entry_reply)) { + to_array(entry_reply, output); + } + + return std::make_tuple(num, std::move(start), std::move(end)); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REPLY_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/sentinel.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/sentinel.h new file mode 100644 index 000000000..6f998791e --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/sentinel.h @@ -0,0 +1,141 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SENTINEL_H +#define SEWENEW_REDISPLUSPLUS_SENTINEL_H + +#include +#include +#include +#include +#include +#include "connection.h" +#include "shards.h" +#include "reply.h" +#include "tls.h" + +namespace sw { + +namespace redis { + +struct SentinelOptions { + std::vector> nodes; + + std::string password; + + bool keep_alive = true; + + std::chrono::milliseconds connect_timeout{100}; + + std::chrono::milliseconds socket_timeout{100}; + + std::chrono::milliseconds retry_interval{100}; + + std::size_t max_retry = 2; + + tls::TlsOptions tls; +}; + +class Sentinel { +public: + explicit Sentinel(const SentinelOptions &sentinel_opts); + + Sentinel(const Sentinel &) = delete; + Sentinel& operator=(const Sentinel &) = delete; + + Sentinel(Sentinel &&) = delete; + Sentinel& operator=(Sentinel &&) = delete; + + ~Sentinel() = default; + +private: + Connection master(const std::string &master_name, const ConnectionOptions &opts); + + Connection slave(const std::string &master_name, const ConnectionOptions &opts); + + class Iterator; + + friend class SimpleSentinel; + + std::list _parse_options(const SentinelOptions &opts) const; + + Optional _get_master_addr_by_name(Connection &connection, const StringView &name); + + std::vector _get_slave_addr_by_name(Connection &connection, const StringView &name); + + Connection _connect_redis(const Node &node, ConnectionOptions opts); + + Role _get_role(Connection &connection); + + std::vector _parse_slave_info(redisReply &reply) const; + + std::list _healthy_sentinels; + + std::list _broken_sentinels; + + SentinelOptions _sentinel_opts; + + std::mutex _mutex; +}; + +class SimpleSentinel { +public: + SimpleSentinel(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role); + + SimpleSentinel() = default; + + SimpleSentinel(const SimpleSentinel &) = default; + SimpleSentinel& operator=(const SimpleSentinel &) = default; + + SimpleSentinel(SimpleSentinel &&) = default; + SimpleSentinel& operator=(SimpleSentinel &&) = default; + + ~SimpleSentinel() = default; + + explicit operator bool() const { + return bool(_sentinel); + } + + Connection create(const ConnectionOptions &opts); + +private: + std::shared_ptr _sentinel; + + std::string _master_name; + + Role _role = Role::MASTER; +}; + +class StopIterError : public Error { +public: + StopIterError() : Error("StopIterError") {} + + StopIterError(const StopIterError &) = default; + StopIterError& operator=(const StopIterError &) = default; + + StopIterError(StopIterError &&) = default; + StopIterError& operator=(StopIterError &&) = default; + + virtual ~StopIterError() override = default; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SENTINEL_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards.h new file mode 100644 index 000000000..0d4cd97b3 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards.h @@ -0,0 +1,115 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_H +#define SEWENEW_REDISPLUSPLUS_SHARDS_H + +#include +#include +#include "errors.h" + +namespace sw { + +namespace redis { + +using Slot = std::size_t; + +struct SlotRange { + Slot min; + Slot max; +}; + +inline bool operator<(const SlotRange &lhs, const SlotRange &rhs) { + return lhs.max < rhs.max; +} + +struct Node { + std::string host; + int port; +}; + +inline bool operator==(const Node &lhs, const Node &rhs) { + return lhs.host == rhs.host && lhs.port == rhs.port; +} + +struct NodeHash { + std::size_t operator()(const Node &node) const noexcept { + auto host_hash = std::hash{}(node.host); + auto port_hash = std::hash{}(node.port); + return host_hash ^ (port_hash << 1); + } +}; + +using Shards = std::map; + +class RedirectionError : public ReplyError { +public: + RedirectionError(const std::string &msg); + + RedirectionError(const RedirectionError &) = default; + RedirectionError& operator=(const RedirectionError &) = default; + + RedirectionError(RedirectionError &&) = default; + RedirectionError& operator=(RedirectionError &&) = default; + + virtual ~RedirectionError() override = default; + + Slot slot() const { + return _slot; + } + + const Node& node() const { + return _node; + } + +private: + std::pair _parse_error(const std::string &msg) const; + + Slot _slot = 0; + Node _node; +}; + +class MovedError : public RedirectionError { +public: + explicit MovedError(const std::string &msg) : RedirectionError(msg) {} + + MovedError(const MovedError &) = default; + MovedError& operator=(const MovedError &) = default; + + MovedError(MovedError &&) = default; + MovedError& operator=(MovedError &&) = default; + + virtual ~MovedError() override = default; +}; + +class AskError : public RedirectionError { +public: + explicit AskError(const std::string &msg) : RedirectionError(msg) {} + + AskError(const AskError &) = default; + AskError& operator=(const AskError &) = default; + + AskError(AskError &&) = default; + AskError& operator=(AskError &&) = default; + + virtual ~AskError() override = default; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards_pool.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards_pool.h new file mode 100644 index 000000000..e6e9e4a52 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards_pool.h @@ -0,0 +1,121 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H +#define SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H + +#include +#include +#include +#include +#include +#include "reply.h" +#include "connection_pool.h" +#include "shards.h" + +namespace sw { + +namespace redis { + +class ShardsPool { +public: + ShardsPool() = default; + + ShardsPool(const ShardsPool &that) = delete; + ShardsPool& operator=(const ShardsPool &that) = delete; + + ShardsPool(ShardsPool &&that); + ShardsPool& operator=(ShardsPool &&that); + + ~ShardsPool() = default; + + ShardsPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts, + Role role); + + // Fetch a connection by key. + ConnectionPoolSPtr fetch(const StringView &key); + + // Randomly pick a connection. + ConnectionPoolSPtr fetch(); + + // Fetch a connection by node. + ConnectionPoolSPtr fetch(const Node &node); + + void update(); + + ConnectionOptions connection_options(const StringView &key); + + ConnectionOptions connection_options(); + + Shards shards(); + +private: + void _move(ShardsPool &&that); + + void _init_pool(const Shards &shards); + + Shards _cluster_slots(Connection &connection) const; + + ReplyUPtr _cluster_slots_command(Connection &connection) const; + + Shards _parse_reply(redisReply &reply) const; + + Slot _parse_slot(redisReply *reply) const; + + Node _parse_node(redisReply *reply) const; + + std::pair _parse_slot_info(redisReply &reply) const; + + // Get slot by key. + std::size_t _slot(const StringView &key) const; + + // Randomly pick a slot. + std::size_t _slot() const; + + // Get a random number between [min, max] + std::size_t _random(std::size_t min, std::size_t max) const; + + ConnectionPoolSPtr& _get_pool(Slot slot); + + ConnectionPoolSPtr _fetch(Slot slot); + + ConnectionOptions _connection_options(Slot slot); + + using NodeMap = std::unordered_map; + + NodeMap::iterator _add_node(const Node &node); + + ConnectionPoolOptions _pool_opts; + + ConnectionOptions _connection_opts; + + Shards _shards; + + NodeMap _pools; + + std::mutex _mutex; + + Role _role = Role::MASTER; + + static const std::size_t SHARDS = 16383; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/subscriber.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/subscriber.h new file mode 100644 index 000000000..8b7c5cfb4 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/subscriber.h @@ -0,0 +1,231 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H +#define SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H + +#include +#include +#include +#include "connection.h" +#include "reply.h" +#include "command.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +// @NOTE: Subscriber is NOT thread-safe. +// Subscriber uses callbacks to handle messages. There are 6 kinds of messages: +// 1) MESSAGE: message sent to a channel. +// 2) PMESSAGE: message sent to channels of a given pattern. +// 3) SUBSCRIBE: meta message sent when we successfully subscribe to a channel. +// 4) UNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel. +// 5) PSUBSCRIBE: meta message sent when we successfully subscribe to a channel pattern. +// 6) PUNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel pattern. +// +// Use Subscriber::on_message(MsgCallback) to set the callback function for message of +// *MESSAGE* type, and the callback interface is: +// void (std::string channel, std::string msg) +// +// Use Subscriber::on_pmessage(PatternMsgCallback) to set the callback function for message of +// *PMESSAGE* type, and the callback interface is: +// void (std::string pattern, std::string channel, std::string msg) +// +// Messages of other types are called *META MESSAGE*, they have the same callback interface. +// Use Subscriber::on_meta(MetaCallback) to set the callback function: +// void (Subscriber::MsgType type, OptionalString channel, long long num) +// +// NOTE: If we haven't subscribe/psubscribe to any channel/pattern, and try to +// unsubscribe/punsubscribe without any parameter, i.e. unsubscribe/punsubscribe all +// channels/patterns, *channel* will be null. So the second parameter of meta callback +// is of type *OptionalString*. +// +// All these callback interfaces pass std::string by value, and you can take their ownership +// (i.e. std::move) safely. +// +// If you don't set callback for a specific kind of message, Subscriber::consume() will +// receive the message, and ignore it, i.e. no callback will be called. +class Subscriber { +public: + Subscriber(const Subscriber &) = delete; + Subscriber& operator=(const Subscriber &) = delete; + + Subscriber(Subscriber &&) = default; + Subscriber& operator=(Subscriber &&) = default; + + ~Subscriber() = default; + + enum class MsgType { + SUBSCRIBE, + UNSUBSCRIBE, + PSUBSCRIBE, + PUNSUBSCRIBE, + MESSAGE, + PMESSAGE + }; + + template + void on_message(MsgCb msg_callback); + + template + void on_pmessage(PMsgCb pmsg_callback); + + template + void on_meta(MetaCb meta_callback); + + void subscribe(const StringView &channel); + + template + void subscribe(Input first, Input last); + + template + void subscribe(std::initializer_list channels) { + subscribe(channels.begin(), channels.end()); + } + + void unsubscribe(); + + void unsubscribe(const StringView &channel); + + template + void unsubscribe(Input first, Input last); + + template + void unsubscribe(std::initializer_list channels) { + unsubscribe(channels.begin(), channels.end()); + } + + void psubscribe(const StringView &pattern); + + template + void psubscribe(Input first, Input last); + + template + void psubscribe(std::initializer_list channels) { + psubscribe(channels.begin(), channels.end()); + } + + void punsubscribe(); + + void punsubscribe(const StringView &channel); + + template + void punsubscribe(Input first, Input last); + + template + void punsubscribe(std::initializer_list channels) { + punsubscribe(channels.begin(), channels.end()); + } + + void consume(); + +private: + friend class Redis; + + friend class RedisCluster; + + explicit Subscriber(Connection connection); + + MsgType _msg_type(redisReply *reply) const; + + void _check_connection(); + + void _handle_message(redisReply &reply); + + void _handle_pmessage(redisReply &reply); + + void _handle_meta(MsgType type, redisReply &reply); + + using MsgCallback = std::function; + + using PatternMsgCallback = std::function; + + using MetaCallback = std::function; + + using TypeIndex = std::unordered_map; + static const TypeIndex _msg_type_index; + + Connection _connection; + + MsgCallback _msg_callback = nullptr; + + PatternMsgCallback _pmsg_callback = nullptr; + + MetaCallback _meta_callback = nullptr; +}; + +template +void Subscriber::on_message(MsgCb msg_callback) { + _msg_callback = msg_callback; +} + +template +void Subscriber::on_pmessage(PMsgCb pmsg_callback) { + _pmsg_callback = pmsg_callback; +} + +template +void Subscriber::on_meta(MetaCb meta_callback) { + _meta_callback = meta_callback; +} + +template +void Subscriber::subscribe(Input first, Input last) { + if (first == last) { + return; + } + + _check_connection(); + + cmd::subscribe_range(_connection, first, last); +} + +template +void Subscriber::unsubscribe(Input first, Input last) { + _check_connection(); + + cmd::unsubscribe_range(_connection, first, last); +} + +template +void Subscriber::psubscribe(Input first, Input last) { + if (first == last) { + return; + } + + _check_connection(); + + cmd::psubscribe_range(_connection, first, last); +} + +template +void Subscriber::punsubscribe(Input first, Input last) { + _check_connection(); + + cmd::punsubscribe_range(_connection, first, last); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/tls.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/tls.h new file mode 100644 index 000000000..0f2303db7 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/tls.h @@ -0,0 +1,47 @@ +/************************************************************************** + Copyright (c) 2020 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_NO_TLS_H +#define SEWENEW_REDISPLUSPLUS_NO_TLS_H + +#include + +namespace sw { + +namespace redis { + +namespace tls { + +struct TlsOptions {}; + +struct TlsContextUPtr {}; + +inline TlsContextUPtr secure_connection(redisContext &/*ctx*/, const TlsOptions &/*opts*/) { + // Do nothing + return {}; +} + +inline bool enabled(const TlsOptions &/*opts*/) { + return false; +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_NO_TLS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/transaction.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/transaction.h new file mode 100644 index 000000000..f19f24889 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/transaction.h @@ -0,0 +1,77 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TRANSACTION_H +#define SEWENEW_REDISPLUSPLUS_TRANSACTION_H + +#include +#include +#include "connection.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class TransactionImpl { +public: + explicit TransactionImpl(bool piped) : _piped(piped) {} + + template + void command(Connection &connection, Cmd cmd, Args &&...args); + + std::vector exec(Connection &connection, std::size_t cmd_num); + + void discard(Connection &connection, std::size_t cmd_num); + +private: + void _open_transaction(Connection &connection); + + void _close_transaction(); + + void _get_queued_reply(Connection &connection); + + void _get_queued_replies(Connection &connection, std::size_t cmd_num); + + std::vector _exec(Connection &connection); + + void _discard(Connection &connection); + + bool _in_transaction = false; + + bool _piped; +}; + +template +void TransactionImpl::command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + if (!_in_transaction) { + _open_transaction(connection); + } + + cmd(connection, std::forward(args)...); + + if (!_piped) { + _get_queued_reply(connection); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TRANSACTION_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/utils.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/utils.h new file mode 100644 index 000000000..f77f796d6 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/utils.h @@ -0,0 +1,193 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_UTILS_H +#define SEWENEW_REDISPLUSPLUS_UTILS_H + +#include +#include +#include +#include "cxx_utils.h" + +namespace sw { + +namespace redis { + +using OptionalString = Optional; + +using OptionalLongLong = Optional; + +using OptionalDouble = Optional; + +using OptionalStringPair = Optional>; + +template +struct IsKvPair : std::false_type {}; + +template +struct IsKvPair> : std::true_type {}; + +template +using Void = void; + +template > +struct IsInserter : std::false_type {}; + +template +//struct IsInserter> : std::true_type {}; +struct IsInserter::value>::type> + : std::true_type {}; + +template > +struct IterType { + using type = typename std::iterator_traits::value_type; +}; + +template +//struct IterType> { +struct IterType::value>::type> { + typename std::enable_if::value>::type> { + using type = typename std::decay::type; +}; + +template > +struct IsIter : std::false_type {}; + +template +struct IsIter::value>::type> : std::true_type {}; + +template +//struct IsIter::iterator_category>> +struct IsIter::value_type>::value>::type> + : std::integral_constant::value> {}; + +template +struct IsKvPairIter : IsKvPair::type> {}; + +template +struct TupleWithType : std::false_type {}; + +template +struct TupleWithType> : std::false_type {}; + +template +struct TupleWithType> : TupleWithType> {}; + +template +struct TupleWithType> : std::true_type {}; + +template +struct IndexSequence {}; + +template +struct MakeIndexSequence : MakeIndexSequence {}; + +template +struct MakeIndexSequence<0, Is...> : IndexSequence {}; + +// NthType and NthValue are taken from +// https://stackoverflow.com/questions/14261183 +template +struct NthType {}; + +template +struct NthType<0, Arg, Args...> { + using type = Arg; +}; + +template +struct NthType { + using type = typename NthType::type; +}; + +template +struct LastType { + using type = typename NthType::type; +}; + +struct InvalidLastType {}; + +template <> +struct LastType<> { + using type = InvalidLastType; +}; + +template +auto NthValue(Arg &&arg, Args &&...) + -> typename std::enable_if<(I == 0), decltype(std::forward(arg))>::type { + return std::forward(arg); +} + +template +auto NthValue(Arg &&, Args &&...args) + -> typename std::enable_if<(I > 0), + decltype(std::forward::type>( + std::declval::type>()))>::type { + return std::forward::type>( + NthValue(std::forward(args)...)); +} + +template +auto LastValue(Args &&...args) + -> decltype(std::forward::type>( + std::declval::type>())) { + return std::forward::type>( + NthValue(std::forward(args)...)); +} + +template > +struct HasPushBack : std::false_type {}; + +template +struct HasPushBack().push_back(std::declval()) + )>::value>::type> : std::true_type {}; + +template > +struct HasInsert : std::false_type {}; + +template +struct HasInsert().insert(std::declval(), + std::declval())), + typename T::iterator>::value>::type> : std::true_type {}; + +template +struct IsSequenceContainer + : std::integral_constant::value + && !std::is_same::type, std::string>::value> {}; + +template +struct IsAssociativeContainer + : std::integral_constant::value && !HasPushBack::value> {}; + +uint16_t crc16(const char *buf, int len); + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_UTILS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/lib/libredis++.a b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/lib/libredis++.a new file mode 100644 index 0000000000000000000000000000000000000000..3f398e48dfda90206cd0ad95d4f11faba86c2aba GIT binary patch literal 1399538 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≷@IH$jl!8v}3!8!h4GdRcp z7Y66}|Ha^Yk!M|AXLOJhq#SttI;!AQzT7W*fZ=oc(}aYJ@VQ@fSi$Y}^iv8>^5=Tq zM2#gbf=}Y3`TQ#e=X!p#g4^jIWB8o@v#IgGWvBm_49@9)gTZI;p62Jr48EVio2U`O zMf5qH=a+#;%fQoR;0MaUzt7-YAD%1&KZ_bgT*N0Y$JR3N?F`Q8?_qFG=dBFR@!!GV zT;H}*V~mURhl)cS`hIMKYx$o~0>-8B zl^OT_h+by!|B<)82W;@)Dmbk}p-a&ozEX!QW$p_bB-ES}Xo; z1;6rnR(Kr|#Pt_t;O}_8e6G{|TZO-_#R~s71rOWcUsv!eRKGyec~HR*+2Bn|?(ed} zX+1Vw7c>8sM7vSJN0c9^>Gvr3SQ+?!<`)qi>^di|=Xc0&O{Y$k_j()r90mVtRev>p zmxBMJ4gPusztINYqu`&~XifL63cf;uMciWterr8cVsO}1t&jA2AH%Pdj&gGZeNJbS z;)m#SyP=B9!RPxK{-uncPcZl!82okxCwWF0{7VW>>0ZX*UuE!J4E{X^=luVa!T+4$ zt737;^8$v?^E+4uK2-+(f--Owt`O;Rep<`mcQg2#@LTIckAjojwlMhX6`bVuMg|{Z z@ZAhvj{mnQ{FmW-C&PaSgXbCieF{$L78v|`1}`%B4GhlZ@J$Bi^8ala_+t#-kKbB8 z+P)G$oW8cN1n2Z$rtBrbIUQ|x?R2!=wbP;fE9kP*(e~I*C(H18`O>!abW!)Z(Ii@$&sPgLC;m$>5xyCdw!- zqQm8Iwt~}F3?D5AI?jnM!sl`bG5Bsg)A-vNoTqzIb6Zu zTn0I@XUadww5aaL&)849?}I{TDl(=Mq5C=XA7RW5@3-gRlJ^JHGaJ2+rvb zFgl!1?FZTE%rJaT=j{y6>AZ)*dHquEn~-M)JZU??+bQm!+(^Q~MdY}Da;tz!IQKWc zq~N4)+;8Fj3HMuWV{}IGTk~@_gY*3UoWVIiv~Ln!#3%39zed4{p9}EO^fxp7aRz^? z@dMVxMeLUFX_B`_|BT>=6pX&~bArE(!F7L*;8Y*=#qr6X(brn_gYaLgf%tA#zX`rw z{pMc;PclHW`c3jzRZ~F13{GXHuO9V-@adzkE7T8yE!RMN{~d#CACEvcGr0Eo33LmC zYnc$}R}B6P^_#xVrwrlJ{OJ8Wq6~hTL=pcd8GIFkU(MizujT#$20xwQ-_77_7`#OZ zg}(d@o?~$O`70&t{R~d&>Tar@YgB1-NoQUYhw%k z;kt*x2|u9lTa*zYT%xb{t9vDb6Ta@B@cGGt|Mhr?>tY-Jh@$@i1}FM@-@A`6IMFXC z{G$v`^!2_eUu1B?*ZrSI7@X+q{qyRSu_GFUKcnccWpH|SK*fX3VQ|8~PT>zSIMLVp z@QpDz;nR`zbRA-FK|dhj?_hAk{|ANt9R?@*dOyFPFgW4sy^VE)N3`jq_k($cGU5ay z{2wd&=QB9b*ZTrS8JzI-9>}{HoM`ENWikv-_`2Wq0R|`fdOrO}7@Y8Rf6TX`uz#$W z>c@96FTY3huY7tmwSiWX`Zf%viYecQ{zAbg02>xqqrJg+jj)VWa%xR8t?Av6&k{XK zF}MNi=NEi|7D4M5ob)z_mbl_rCXPk-3nji}>b?!b16VjMmR@4poc__Yi!7?3?#{Gc zaVXZZvy4rQk7o9yNB29qvSr_cwZE*W%!RVGShnk~J+)2KA+DON1U5zJwjxTFtEfNF z0^mZk+}8EF;$32|k5N{)mbPqGb30Hd(heFzGY#&roIN$Vq5D#Rhc;lRs>#VD&ao?G z$8l0zaes0zcCn!StkUB!F*rdky#YrSyi#n8pzg7^)dt#UDxEJveFgFKNOFj_(SbJS zvlBVkGB2)A!=E_Ah0sO?Q|8l0@q6+OmHT0QmwP{^-ak`))B6`z@ILh=>C%2Vy`R8m zvFTIam9EwDOYpx9zZd)dq-MA{^qOxgezqe)7~=|EO-#l>CfipVM|z9G2CrnI!6t5iT_MVF!o9RgDEg*T!9 z@Ao<9+&ObI)0W!Z|NpoD-}xkS?|sfW&-0w;Jm)#jdAax8qx0MRZ+Hv&cjTeZ@u;8w zFkp01wEKoT6?W_Ez4iJ=UyME)=!v)?&UypT*YL;GP3ybWmHNx^q$!UX_nitD>FZ$+ z?o_z0!QACuwL9ej&%hmV9&Ub)52gHcvaB$w>Dyy(M*GXp@n63DTXlXjjy0(L`HfE0 z`{)nq1v*z}r2O8Ff4}@VH%a=l@Nb9z)b!6|q!rAgxo$=r?EUG|6}p*=l{!qk-ne*4o&~Ph_i#qZ}<=5CLjM>b^hn<%((aS&&>Jw zuewQP9Mk#9KXum7H~M>#reEYM|ECtJj3J%hT*#{(cor97e)(_J`869}7C--7AIiu7 zUY)U3=Oq7ZtA76f%h1>9%;nsOy1R+Utw3Jwz(d`Iz752^s}1m=9zT)(yYTO)|AMCP z6QA=~R3SXe7vZO0f_TFG@+*(2{Ew)M_v@D**SYe^?}0C=ETcLn`48jY&wrDq@7Leo zYWgSW{N`f#H`kp7_;=V@Bbe{ps;7ziyAApC@xSsGmBZ%$JplRnpIw|^eogG`5e`Ow zCi3T_zc&y4Q-IG;|1+Atzx`~bvxPvLX9s`%-Kp~zLk@PhOXoN3)WjM3=J{I%_#dEQ zLU7eZ>{k`;O@4#-zY5Ubt?4t59ZY@$`_BUO$29#`osjxRTWInd`dmk%F8Spr2aW*# zApY$TwjXfMbt2~X%XxQ`O6Zrjc{bM;o!>A2JvzVP*O2u;Ux57hbNu~Z_*Rv}l#}J> z6p_hq;DefejY{tQ?$z{9^C0TRv^#T6>ioWBPUwSvNN#=;`+4N2iSwzSQ?B!eO+tM& z{2TDMG<|>m8lC?xUk2Z!b35|mE^>x0MU#Vq(XCuz!H}VtR@#W>?UcJatsuuyc zJ(())&rkmdDm)?VTcbeL$L2;Exf|#-+~mt&|8_! z(#@KGc8!9xPWgBwwx}qO#TaltAZ@|WGS`92%$<>QJ?z08Nn3^}VhG`%ZG!FS9r!1V zMSmy$kJHbT`SJRmeep^7XP?Nlr|K*9wMySt6N$)k0nkGRgbjdHdLMNk;{s~Q1zvwq3ZUnq3U&agsS_;Le(30 zTZg5NR3V+4+~d~mNE{2aES5X8rScUP5y1X&0J*J(K$pD9d7^jo_Fn$P(m`?=!|B%{ zHytV8HBt_YVSu7TzuV^~M=J0bOQ#8S(<3!}O1=nq7ktY&I`r(QfU)4&gXFJp)9rh4fa=9OH2qQ>ChtU3IL9O>|-Jxe+ag*iAUq(~0V{sQNf5okSC4F?UTfK8zrudKUm&gBcv;papPbAA5QZ4WL zYqF){uj8BKtk~oe4WaM1yz9+1fW4XN230BRsGQubgePa$|G)9vaN!SFhYc%Y=`&dY zIKDoaS&{s6GmgxSrTfCD`#S)RrP!G}FNLPXDoZWp8LqqyU>>&n%w1HCy(`~f1!V*& zfG>s0kpXADO6;0Y)waE7BgN1gMWJCX35Bkw!!hzgXgGxH&>Ml!$jvZs)TMANEi~Qa zFM-HAIT}a6<6QH#ILtbAu3NV^H2e(ExasEdKZd#&R#v%nzX%O~2NuRn@i^b&(6q)< zcVvHP_*TM_a-w_-GVelKmT2c>q$ITNMgM{IWTHNne91?>p46M%)YZrX@?TNpYlOTj zX@2>SIdbhU}}U5kD_>4$i}eUo=w_9)LGr+7Ub4UKGMDL%1- zRicg~ZukffBpxOzh_5&_(vB3d^evTx0DyUY2z|EA3`>lgzP)l3DZFBoLi{MgaOGw~ z(_o&0d~d#zyh}=QkHV9@LE+h@aw4-5sgACDF*H0E3eTblJ#!X#=43!aCl4}1EWNt2 z71ca}*M$H>*gdAIzk|eHRd)!7w}EeeRo_GyL=BuR$P7_;kc_VCR){(@y!A~owGh!B zM`?hhy)LbLCNw-%ihhHV$H>Epce_Kc1wz9@&pXOlM^hJ)%~d-~jsq{MV6v<{*o}-QrUr1lM^Q>WL!+ko(s)5=yb9f_KAp4fXsQ2l2^a6y7AEPDvbwP<>7f9 zL~J}&K0M*f(P(s|OY43g8a`d;cbWf0#mAR?`CU;9uS8JV%n~5*^f`%!p;|%m#Y`xX-6%m(aa6RBk=@^x*smupWJu_dA%GbespFo5Ql0On&U(EdCb@hA! zSRnj3!1tnDPR9EYlbyU@0KAtQ(;f+ESmCC{rBY_LMVnTf*^MI5$wliPN6T^SwV1EW8dY|ZHZBp!l$R@8Tu@qzFYdBi-}5DCY3t~ic;@kRe!`Z{#~sntgF+IEL&Gz)r_uLGt;mq z{qNz{PtRZezgRy-p^^V>^>b0fg|YO8FddZc8v4GEqD6I=(?5Z4G9T8=YCyx4RRGkE zxyiHKU5iVpBs&+EqaW;smM>-_!*h)@^eWsBIs~EXE0BKV!O%!6%zaE96z`_ROF+N> zCVP=mQ6t?PV%qO`AT<2$p!AsHbG#86=7-<>?d(3n4&-w@=;t~eGc*JB1)Yx2@MCl-avYBgH^Z{MzY2fi7DzFczNc~z zWanD`glb*v*Ci&srE(ur&=5g5{|YBg+zATLC4Vo1UB7&OLs-(_zCmGtCm|9?Mlnb_ zy6&OGG*S~P#w?LB4woP?&w0o)#u)ZN<-8iPU7)IMxRM@^NRA#jILnpXAc0^_aKOnC z9AJ&P7&L1VO@Crk+=XG4&ajuc3ec><8G04&?FFb3OVL^((+P^!>ZW7b>wzq}wOzbR zcsvl~PQ@;4ch+QJd;mSdjBd@NhD6jiAqWc)s!t)VDbXTF8Cx~tx zfe;0M-N6)E{T@bU;q*2FDYYiWX^zzR zZAvmK7{I7tKpis^gRbMPxOI*CNg7kXRKGFA=2t()P=>pCH8R|5IC}t4k>Q1q>I4~b z+aXyK9AHgwfHme~(5#K&mc8|{Y#EZquogPG9$1wp_<9COyQ?Wca(&i~aFdijsPN6#_{3w?__+oXhhlU>(aor%5M7QEJ#o#_P5;vKHP^X!IQ&o88?eO-L-zs?dAym4F zdnt%4-pBLdl{9(q?m^znx$qhl-lcVKhK6TpysSCm)#Hf0g7^2tBxEI;O5%Cls`4|F zvi@=G+yv`KSTYW67Gs$Y<7UB}i*XRJOpkcf3!%0O-yO^-J~fc(5dmLEwLLXBt2QQ% z2)^-ZPB||VEH{XOw8}|M%OcXzIk=RK&D<=tOa_rYJC4Y);qdDQB-t>N<)jTdcy4blMiLiMzm=!+5#uHC=+6g5K?^) zEzZ1gC#>Y4wqQytdd09Dd{K9@mjbGUFuog1gvg4Owu~{nqHj=<7 z<{fB$ivI%RpK;SZ8NUy?RDP-=^Pg_z_l>t+mz?5v+vBD;P*~a<|2S-}C>r6)I4L?t z(=*e6jU}D}iSqghl$@^nZpen$qooQT%Ituv&G=I^ykFA4m^t%h+r`fvf{l_N8Gl{K z8PlxGIP`q08a?;#A%|CHM<&x;fhWQ6%ij>y80R;+GMXt)J6BONqG zTC2phNG_?1?FGM(ib+Ri7qR9eMp=7W+|>A5xT%xu(fW}hzEm3`v#SMV(5 z{r`o2V*L4k^)vCmKtIvwCI;G5H?_EQyN*)N7<%AUbT|2iJ2VFKhUW6|QY-_(7Vb*C z%}wFPm@^plOQji{vc{=$R&-r~1CYWCy8F9U^MFq0V&_bJ@e^v(~^Ag z-YPJqh~Yihwmuwhg~mzoUHxmp7SZZc0xi=QrlL#&yc_DFSELm9@9r|l%!~5|Y=syc< zQxRu#q#~AVFJG{;u88^MLwDw_L z$_O0qU6?(@N*Qrs(m?r>u~cQHo7z}9ezU7)G$g(Jut?G54%CD^!a~V)|Ng+#&j;q> zrSabdq%J>xg1l$H>3RiRBz-7?i}`3qQKMOjuXO()o6b201@LDjGmeLuRe zXQSvVOPSG4^_8NWOOtc3Ho8;My9@mR!+Fuukt$ku|AuPCoDnyMdr+I%=J8^zI!bM0 zF~_bk(f{QA(bW4Yqjhg4%AzT4&8`y$zqrr{ERgTV4@H{}+`(4d=Y7$ZFpHXi~KgOm|^HSfxO{s}bI?nmIF? zC(~_N6_QOQp602%l%80CtHYQ_VOQhRIt8>e&a^k9aimNP&^V!&m$+ed{2w$yPvhW& zd&SIZ+>6;_{@&!}S{_;iyh%^vY?u0grNexx6s}-7fLaQXAG={;{CghzM%(^R^M%+Mhv!1pTY z3WXE!g~F@ay29OEYp)Dn)*il~DY7iw-5XvWUF^2R`NWO~;UMW_kwrPlb^m5;qcV}! z#t#gVWeW&8mb`sEMo^RCWhkA;mY9^JmY&Uc6L3LZ*7zk^Ecq8Z-dN@AWW8$2NQ0E7 zL!&37w|EJ~u?j;;6!98XQC75@J0#_|}0t4lN6OW!; zivFD&vt$n$6CEWf)gJX4eJ}4G#LCJy6?K%A0#=28y)D^!2>G1`#x=fe(!zA{mXaky z!Qu}ErU3WCsS3}4#*%EDVp7ME3R3P#Inh9(uofni};7;#EE4X6^?i~KQ-gYU0raV#c| zC~+(#jz;2GR<=Y}ga zT$A-8uhDSs>9)hLz)dfmjY$gJ{|1obT&MA{zqbS5g~@*5CL433hMRuf#Qc-SBb&7h z5Z5nh_`56^Z~s-pXZYaX)bL6l{Cfh|m14!IvmGVUg2HEz`p}hP#e4*y8CH8VzRHyX z=eBYc#9zTJ7o6$a_`|J$PnXl;R1EzYhIgXctSc_IFCAACT=!#?=WHN3_Lp94V= zPrVO*1K=|(dnmpdIQS*t`RePb0_jhKqRz1FC;c{t^?>KYb7ukg>jmJ)Pf`3gY5vVO ziIxGL5C6&n@V^6mx|~v~_%rt4+5+i6QviM&;FPDIpI>YE9jFHe`f3cv!a-qrKYXc% zn=?X9`b`Dk|E}TtK!<_88pD}T7~=QCZ_@A@-IVC_Fib($$n<{rl^VXAFXAAW^qB&1 zo}p=UQ{_;S*hYCwkj((tUJQq`3`0kqG`<;!TS>n_6n( z>zbA{&yP3N&W^{|x5d|X_9gJ)n#&{Wnis^c!bdu~`V#yO$IKcs0n&?O7c6Lq#pBE3 zwXs=qmqiw13+9E9cy!sK=(1?bhvLgtER8IWHu7NRiOHKTXpSCw@;MW+H@7U0E?b-z z`J9Q!t<>JR!h#!>U+PtOxo5&uNW&t7nHxM;i+}D3u zJR0YxXxh3~$GdwHoly9ht9p8znXyKEq$buln?E=th`;=q#h*F+!AEamjrfohBILPt zuG|Z_c3zI^=x<=n#V4(7jZ)yoxrg~FE8=?VjlXejw0U+s62Z5bpbWF)#InRwa>de| z4IOQXyv)icri>A|V9}yx7?PHT1+fV6;K+6#BDU1kYn#@uQ@m!6X;*lx)}=*MORDkp zeO*0RoK38Wuj;r$WqqqQu8zyPz3pv@_Q>*eAdltVwpCXUUwlnl=UO85@w$*-9GeI} z{I7K#=<`SG+Sje>xf0}HqhZ<;9lhNf;%mDv$GUWTZ*O-mYR8GM=-SW;wOrob*N@L0 z~)=83+A%Fkvg%Jq)>*ofkGMb8k5zL+3N~sDPRkj z=PmJd7%e0sG?X$NSW1V9ylr3t^1K2%V(1G9^Dyyub}9A`!}iSb*&g^XYumf1+J}Lx z)`yIZsb_6rx2G@-*sz*-ORZSN=DBg$#ra|v-#SzCdfWTpRuYJ$`a0vQ*0%Na#ks^C z$2Pxi`im{m=0w&}UImi&F67?o{w~GK07VA&F9Z7xGm3Bm_KA7pIq}W^*Es6e)&9U!EL%PTX37t z)q3LE&*!xo?x%abmEOivrkC#gc)}X)uiq*wy^ZJV7To6lKMLS^%1UqJamy&RiS1&Q z1-H}hwcs}0NqPy#&wrVQ`{kdu;5MF{Ej)HTe#J^}^HZfKWBu~nY{6~)dAtC;S}y?- zkDdM#1>lbsfS;--c>Q?#3c&9v06#|0YZH&H=T~XCU(TPj(%bTR!GhazK1MHK`t|lC z4JTb2&&MsejpsoNZp-a$#{mI?Uk-O$aJyV5=miUZxn^j%pP#e^xB2<5g~!&Pd#v-S>b27s4~VbU0(oxlMkL&*_~T_@E#36+p@RB z|9cHT&j-Iy`zw!W`)T;W&PWMQ`rs!p0mAW?z7hX=4S#n5_-Q`4Im7H;UGIi&tM(6e z`QQ&|_(!xnP5Lkei*UIQZq6Xl;*oEqor!ct&SyxjiS})-1wYG5|5ovEpBn?&v+*nt z=tR2J8vb5fZ9a{EOFXu}`dtf;t%vt(IOW6L7pA;FveM7D;15}FyWRbl1-JQs#)5Ng z+~6lp2J#ufzkz?AKmN8ZG=T?hT*q7CdUf z69wScYB>2h*-F2~N`Jlu|EdMI?a8+-xGm2z3%=CCqt#senXm;v#Y(@(g4bB^b1nFV z7To6NQVoAEuFPxXv)Y0$v*2qroN~Uvf*bopxQ+i-E4>Z>h6T6f@Pq}o)Bo0jH{ss! z`Gy6z<#v=cE}L$pH+x0Nr!BXWb$Y^WxtTqq{&JZ!+Wc}mU*jQsnMHTGh2NH2uLZZu z%Y9P}q-)FPpDnnZ{#FZa%V(Pfx8?H_4W~ZOz`v2_<5qfGKEJi#wtPe}HJ>&=X8h}y z55Gjf;Fr%kHJs(O<#VMXt-ZK|7F3KS#Ou<-@+u;4%5g?nf+mC+-=DzY_l@eHVcUOmE}= zm_QZW#xtbhhR>7qbJ~KlPdE5KtKok9U$Wpf{;ykjC^v)0_>H7{ss+DCr}yK3&Vt+i z*zYX3O;>}l7dcx;2F5Glt@tK>VzGe<1uE3mDe-lpAGag2736&;W65()Wb(-vpx%gqbk~<4ipgel`I*+@-IC zo3azM&4Qb@L(o18&OBzHW(f;~Kp6k`=;sO_-0agl!v{BosQ1CmUbq$?JWL`89X@!C zhMRX)3_tZ6K5C^eQz+#3KP-62g8$rt8y#S@;mhD}()f?pS6=iKi(tFNg6k<9ft$0! zP5NU5>NtZ|dfu9Sj!6qn{HN>mV-}qFU()agEjZJganv3Q&RerzGGoD+o;I4n^y7>s zezT9hnvEZUaHhY64+!-ZoN;C!B|j#{VAA)gNADdHgWv4OSNqJf>95i0{b!h(eV6tb zrsT)7Yr0Vx{AOSO+blTK->mUhS#aht`!{PXIMd&$(kUfR+fG12^ zhkfRpw~3v2y$KxPr;hdO?CMO!+j@K3c+Q{7l79)=cf{ZkH~^8yoc@|%9g^wG>;^%nA*d8VV0hk^O4@qeiN z4=@vgO`l_eL(RWga%X)o(x<nFHMZ|f6 zcK^JXGjK973;M zSo|CMW}I2*%)b-3=!jsCUHEMH<~;+?%OT8vW?hZW|A?f@{`mR-UIG4V^gv-$XC?pS z+0Q@el0M_@Q2cK7#4_7~X&KD5Q|I^dAJ+MeKdy33)SuPJpHKc{n*TAKk^E65{QTdm z>HE(-drZ@}$CN~6_~t!BB@72)e*5>d&TsY?Nn-qfTuwL{`Gx-+sN)PaDZoCBnEVr^ zpMP`ao*(b7_o)28P#5pl&woVo@2|hDl`6}JyqxOB&^LHKRG|D0C!-L4uP)xNU;kPP zl)s0D5y5;{g8a?EzoBp1&syYX9pRD__G$Viy@@o>=6ZPn`tx_NK8HhA3%P} z!mmH(RlT!Zb$-?#Wn}Uj`LR5H+RQniC$eg^ z1H`#t_Qn4Ej7Aks2)F5p(l>kt*e^ehUGn9x_=rOJAz%6Z`Olg)J8Xx8o%wh6?Ad3} zmkft~=ASxdL-A8DmwKEI_-hg`nkMv3@}sQpM@*ylyKHB>xlOh{apO>t8NV!-A(c84{tTd z7q9UJOMJ33`G|Zl69=W-hy>QxJJo4jwPSk$#s{2V!2xN!i>0o>hbd2wCU?c~>C2i} zGJ#{*>h{J$i|}1je9&}n;wYDgwQZP$v^b`zB0BU&D0J=1AdfqIH+voQOg?Uk6PaSk zeN0moOp?mDTWVL*SmqA;k%l#5WlzjfSgL8nR_ra%=8v>FL}(3;!eIH7P`~fj8B`#lCLnG z>!w!(6l19+rEcAiLf76FWXjXs>Ywpb+c-4?M{6)?tDF2Wsa3dvpSwe^qUgUOPC3|v z2MFk*_u%WVZt{}Kj-4eHP88q$!$(B|_*gZDp8raFiWDL|Lz}Ykd4@orRB1jKbGu;cFs3SK*_K%qe;hnf``3 zzkOIAtJ|+WwAqvchi<(*;RKwN(oOGcxS^3_At@YMK^5ltjQUG&a&Co#@9Ty}K8*y> zwoF>SPd`YZ#8Ow6#_C=RT|0%DQ^it`N}}mA%4307bSY=E7OH1-A0z=`IPQu9_UF;07YnRqm@`C3vrbtJd)B!lrCBQ)f_B z{D7!bxYBu$kP46Dri!$r(i%e3kXRRIC2J|G1l&QPDy_0$r4_22R#awacm=*+PQA=l zXl4OG*>CbfgYfm#L}i8dSz_TD-zCPWVpMzRvG-x#7?chlY)6XBQ*vm4s=ptBIW)#h z7gjXQ*$N|1gobZMeP(`*f@Z&g?-D+baE=PsjJiC$>64OwZ)UTVpDeN6)?0i*!Lxip z?-M+l^0~bfrN>!)W-mCi%9mTTRvSeR&t&rs17y=f(ONSBVPlwRVe62e&CKw@TJ`wg zLz5p#lr1LnB|(zSAJ*m2TA|9phnjr(!s!6Y2Yoa1@q!YsJX<4{+@IMh*OxO6uJXBA z>x@8jav6&HP~hr}x{FlzslV8n)4#jB?&G^r=voif$>V?#T9_t>jS?;MXnZz2230hM zOL+lfU`#sSbW=cj-)q>Lre^c(u^x26Qk5>dLRzo+(ZFNM1^5p%LFEFx7Zw#CIG=>x z2JHh@bFElSbo*=^(jCZIg+=IJ8iYHL&o{RH1Lc@Qc zJaMw{&}%b7!;j$MF60<=i%@2V_e*+&StBBzv^P-k9_@vVI>h{4W-$$n;u#Yl+Av9cz($Ha_ zA02uVZcMYt_)WMmov{Au!~xSH6O|pN+G-9|Z4U#7Rc&KHq%@v8QN3!DI?C3YS2e8- zFfC!~H{1J2RNMy7n$OQH24GbDx6gqNOhsmQwjUBWH+0g3P?nj0h4Ejb(o=t!{%Vo- zAW4V_{teIL?~#5>q;5X=Enb-F`bC+_{gua0>}Pmx+2~KwCQQCyx&A!n>Xt2tDHmu*f3emQ$alw~Zb7@{Tk8gk}Q zNm8%m@?r2Xeu~$h&!=#n71cd7$()ca2mP7(h5B>TGH{}yIf;U)p~9`1O;TpqNhqN@ zQ%5z}Oqku5bwq<|Zx*0%U!K2@w~^)CLoBU6V!n8#X=~_aAn`O5TdsIkUr)b3n*4>= zccU-Keav?2Fz8k?FW2ZA-Jh#5x}2%uTBCBbi-(;?Zj75(S60AGhow(Ie|2?JtnQW2@Eg?pbaSgY z4hv_9(>ce17p*w=qY0;>uar)`Us|vnl#u^CEInQfd*em&?+)D$r{s5`YX^}%nu<8l z>gS~>R7f;x@a+cnLimb%et%hU{}@`8>bLSU-y_Tq3iF>CL}H8oe<3s_P!ypDMHrAG zs78LEB4D6G4MIg`i;=06wuE;1bZF#Jpys&e8&>=BRr}wQI;!1GUyUy3_u0X(=ojrF z@Z6yBya(EC#!r+88*BEs7&;8f2x1yRsu7=SH$50d2h!!48H&F{$ZDFnm-QAYZ3~>s z>_LL}K$u?tCr3d;RdD9lN@LB+GjGB&WXD^Q2KkSZ2)F2OP1PvP zM&VJ>`!j10t@}}}eYx>NiZt@mE@jX3y1Hllb1I(?-ju0_j8(6ne9FbhRy8>^+Z>Cv zKf_bV$imMk6f4d?ff{G*>iIiVpreff&YRIlF@N_W8aF=1k0aNtL&DwKv0Z2Y_z1$o zJl0H|LIJH;p6wHqc&cA}wl9UQeI99Jsf}p)NE@wwjEx1-SeFs1n$t- zFJSA$c%cG){A7+4(CTY2^}_U3X!Ud&fJ0g8)SOD~&+>$il+&;aBL>HTh8?ZsbS}np zq2X(QvmsSB^eVjADDHoNG=py7K6l%5@L~B0gpmjPC*$?L3@~98 zVgjn<4D~ocsb&nC8cWgs%ccDB5jTTW)BfX(4~HtQJESo2!Th+$~Q z%50)M3_!0?II30VYhL>^qE&iHO@)h+xz*Qr){L?})+ql$$cIag%sz!iziZut(1^HP=YvEL^huD^ld8g`?&Oi>9 zBYg=K@)C0x5^4)mTs$8G4k$*rL?rzHdfgx3%TG>dnqy+tB%8vl-ty42dOZR~+KQbe zMUHOi>{9BU3=J=(DhhA>}?U!%b0rX{@uYVa6g1g=1 zkKApqo#$?QWwKkn+wI)V*K#o9?soo+ciWZy6?Cb2?$EuQ{&lW7tJ(d;PTu0UG0oM~ zi&K9GUAPje_cJ$npQ^DxOm-E74sZooTr3UFqDQ9G?U!N4j5jKlqriH6KYtWA)GIz{JyHGXm@WeD_KgS=G za$5Sw^Gtat+0gU#Zfaf*LGd5&-AWqQoCrzrPKk+K$xgWiJJbCN_b*6iDO|mD7C8#W z1+KTCV0W*&qCF8#bgrXy4DYJ1Xz%V%K-omW+@yP81-o>jf}NjF!L&b(Gx|^iMko_J zYAJOT{ZwjMDW;2uKf|KP;jU~gFsf`oT*wor2h(`I24otDG#xl$?%gO^cX)aGYAM_g zI{Q>RN;bh(qWw%P9>Hy2~M8@0*%KpT)2UKL;npax5m=VE8QfHkKWZ-1@w}vCe>KK6JK0K zLKg{9rnOj)tJ@tK=E+XX-vltS$&_JZQ!M@6%0c9frI!rElFcq~HQ`ZGu0+a?osBD@ zo(as$)IAUy{!599>Os_in;fnjWZG0?4^M30iu+i4h(;uqT+#tlJ-8KA18!<)Gwydb z4j}HH%2B4NL!JQsPEb zNCZ{Yh-!$S8dwz%xXGoEeMDp*OJBMY?72IMpdS4-(%=$tV|9;*hF_!wPOWH)V&QD~ zlYB@vqI&vJ56!5L2(D)QCXpLmH`%`uxSPNbWV&JixI4rHigW;Zht%j%lwE2?PqgmU z(C`=F90Chs`%p2>sGtb0dPsG>L~iT=)&XGcQCQOypqu`zRLo?c8sVLr`Zr#rx=&-_ zQ=c8hbCk-+57E220#e_R?8(nC7J1c^szFXK|3cFKP*5hnC$YOmn3e%WNsZiw8yu$@ zPD&ky?i`UClX?TbI~bc(^_CjBOFi*|gi>8nY3wX!oLYF9tHCnt3Y;02V)?U@ z5vnT^4oyQ9lC`~}bRT2e;|avDQZ>LUOi8oZh}88&iI#(TQBD+#)4DQR_j+jfK7|r> z7IA^HNl<=IqiiC|R-#;~Q4$YoVe51nZ&B3c<=KLDixgQ`V{Hhe=>+>Okflxhu46X6Sie`c(1g`}}nXsGO(OiDE) zH>KKYQYwq*jk7szmYQ;rhV=mgQdf8!Q;&o)Et}J3sVuHj%gL8pnZh_X9F4%PysV-I z{ZWv=N$%2a3=VxQW%v%jtHrDI+LnFNu<0p3P1zKw$VEh+pfmZ8_B`<}sd@^&b zOgGk3o=a-DiZ?%#!jvEU&+dx{Le*cWB%7I|)Kca`k$dyXP&Fz&b34+Za~MVf77{JI z?!M6QcBzULP?Luf^=dAXZdfe&U$WsL21{HX!zu-OgOf~OhSabiE(UFwqGmkqFgQC2 zU0HLJ8U$=~Gr!7RPlb&lJ#K1(Rgkg7`xHIdpOO42Nx>(_!ek6eTFQX}g(Im+2DBJQ z*VavO6|1>w=fZc?AU=X+Pnkj%xqOLg6{@~P^d_@HCPYzQWtYjq9vU%aHdyLKii;DZ z)IZgwh9@mt#Ijt7osmKt8-xuMil3P9Bc?_bUz9)d)$F`B z`It=^j!7(ej8}d%(5qoOMczV_@u_1dgB~sKj?8^I^$Ri8kOo|69BeMy(I;AbE2u5B)TyuO2)p)>X%~46&*25`mTGG!{2(Jneu!D<5pn8=fhyj+OME3 zrt(dU7n6KB^N`j@ZScj%60eIx({fdXl(V+p%0Uu$3NzD9c}b1yu|a4h!Kz7T&K2__=_56p;YOgPWp=|OzMr8wJJ^hb?55}cQ1e_6_ngq-Kg zGjPW`5b)=WKGcAtYc(no1NUUpF*4xTRbS2{4afk@W&SbyMn0A6XfK0K;Bio#8Zx=!re7$|oOhM@I7J2b7X5-#+8+Dyc-FQ0qLm+>_4x zT?`Vra$Z0DLgJ{Q^CpFczt2?Q$(|bGM<6IOZxkqs1?QV*gRA*|(JO9NmD5=BO$foh z=9>`eS!?tPwTOZ2&ic1sc|fI#PE@M10fOF$+%o6olVFZC`QVLCp<{X49aTbYo`_Mk z%!ZCgInST>#J3De!WV5IR<$reug%(X0go~f6t4UdwYAV|ry@_Ts6l*kR60(!N0;d} zS&d10nF7*094(wTQJ|FvmEoFfcca%A@9jrT=8AP7mb$zrmg?@X#{1Yb!G()UDr-2l zSlzy+jfD)a!8m#jwhEn{U~#Ls^&L<1xRy3KG|Y+nSSrmSQcRg@`f8?v`kkd*9mLW| zM7x8@=SYKlm8p*+H`Ztc=E6oe18yn}^{0i3^@8l3U2h*l>ojvG7-f4tHOgT*)Yk$j zTF8LqO=ktv@QIBM3mO{XmaR2-Ea_hx8aW*g4TBpeB|;-ykB+6j$?{=SLE?+C)K@m+ zUNJ5U58kpf%b5HP)iAeQ!&StAnf7{4fHZNr9Ud2!gRr%iBCViN(?!H`+1=C>Ox*j7 zeLanRleX<^lI}4weTLj@n~201(3_hbPw08dL#Ijv^?s@#DAv=X+H@eQY-07YP1G!O zHhHxo3!PF%yS*REy=`HYK8t--bLgp?Z-eY|bQp7TlNECT}_y-K-bVI=tbt zC#xPQWKz)6AJn8IrHJF7^!yLn;s1HNu^b9V0a(4)c>oYP6U}+kF-ty2+0~Ljq>wO9>d}L-?)ui@1q#I#I+ljFQ7%_>$ z@t2MA%3Myb@i_exV;b!k|W+u@nS^Dc-OQ53Ko(t2=0az5E#6$yz7#V=8$< z{*FwWVrH1^JD~68d~6)zGU5R>N1*TeV}+Iy@!YyyYEn%UGTWi!MGG!?4=^5^0eELeVmAyXF1<+1XnU>zT?9m8R#oS; z{e<^b+TMh3Ca!;~o`vHIoT$FBj}5ZE^e5d-tU$S7?P{z-!%5P^LyiOQf>uM_u}+R= zbId7KcTb85%N@7J`I{;?v|`VDq^Vc`$UmGUlEEcF44 z0rNVSytM^i4I4Dz!(rARoDr>{lvRJTRbyc)to9ze*f)Z-##4qnBAZg9b=bPaJsWOv zJFN52E7-QNipH(_H7;VFf--VPR;=zfp=)OYz7!iwxcRUtTKy>Z7}Rs0YCUH))CLpo zCaXs#WnljTp2Qk*orinGS68~q!lJJ0W%q~d5nqHHGNtWe`ZYAH>7!sT?*`(mzqv5k z?qnWDtJD6O*u8nohy9@?Vhsn$>jYlZ{6Vbqx15E|TrMeLH?3V85V2UY3QD8NXNpjZ zkYwgxgfj9`A6?Fq;q|cB0*RjND2Z?wi!i-SZ>Fz4bEo74o1Ub_du0{FC&+wGqU-bV z&EkOXR(zHr^G!pa^G;1_{zs7;zn-H6J(yGbqs;4w)@~`=yeTE-zDx*187SXA4ev24 ze`!CM-TAqjWC5+!OALgc_rOfCie;%ra7DQ^FrhSq2RYR=u z?$ztONuA)P&nR~5#zVvW7%P*hp$~^6;qkfI`jBO0Y4wZHTXPv}XeU&U@0Vh4nLD&S z;MV;*G<*+CO6cBq+>3?+Q}(iqoK*c|UsIKPZlba#G~7z_lm;FyO>;0)x3~XhOqan1 zgr;3sK7KRGVXTa}hp;jN7YUZXo?)RBR%m-ZPNS)%rLlD7)ToRIF&n7r z8`e_o!h2rCf;828qILffy7mXOo!o?1{S-}e{nEg*ZrwIFv}jv2xg*Eh`#MvK-NY8$ z4JV7i9G(l67t_JBjOk*XB?h4$taG0i4endC48K?H8{&#lT+7Nc#{uQ2HjEfx8qL>( zJec27B%q+PW;F9>)4sXmaB1D2Lf7&m3s{MP4!}}Ky`7&vBP_2e$g9KLA-uF2!(s5P z_8kTQ;l9KEtM%)%$c8Q}zbW-40Mr*E#oDDm z0?oRx^pBxypuGf@u5m+MPpk>8#js`pwmB7Lo-^~@vOjGdaD(>vC$su0Tk7^NP5xDD zpBeBjhm)sdL6!CV59sd5LPTY0Rw>r8rXtjT1L`~AdV+d!X01>k1oMYhk1rXk?d|TX z&phJQBjg>L)>WR_4~XY~7N}2o=hs`e%vPu{oB0ti)Q;6Y%+{Z2K&_*5F;*5f_Q>8xaAp}x<2bEB@*X!y0aIsXWn4aUO6!`PjcWBo24tSki9Ug&k1)5))^qg^ z^WYdYa+#l>K9%tfTT$s;@~yb*uNm%G9fVgv){DZu&y# zy0$^kH4Jz~Q(sTis&Q2`bzU^R7+a5ag@(t#MKrY-qv>7XSr;Q#_j>;?B^O^A9o~U7 zw3TR!`=diU!m(;S%&M!b42{ebOMutLTIK9sgcHbs3LoC>j0 z#&PGW+GnG;tFXn99M+#f?rDkyt*(^7dfwIWu9bI9cX6(N4d0er z9Zf4$XwWN6QK*Rce|!_KYPomWlDEq)J7C+& zC{KSMXFSS70QI+r`n!if?#KJA?4H;nv7Y|^4ElSYR5QPL72UE_x937qkdwVDhob@&?VNq@cXZIVJHJP$4Jg*s>FRLLZ>y$A@bCC7UB)J(CtuapV&d;YAt`_O}MN*7K6n` zJ)e6s>X|aFj38geS;4&-sy|j`m*Seqm)w%dFmsec=9Ba9Z*hTKSytLdv$usLGcRQK zXFzMk`egAO1$A+gKh=`f(IPl1slh{Ak@;AbU*M@C!7QGq@aRo!nrPsG2i#J*(%=ab z&;2?4P|1krY*jA4JP3Zc?W#)l9tmgMX@%}x6zm4Iflli`_4P)eFcheKne*WE$^Hi5 ztyXxS0z8)7DLcnJwZamlG#}NLrCAz0RQAl3;u6<`A!D3l&`fCu+8D%=yE4s4rRx#t zRG&dh!or&FhrZ=cuNqlCJTzABCNUB5ajbw~KP+t9+u$WyCe5rjF$cn7y8N7T;!ESJy1VeyzbX;#>R-Dy+|`{3uWwu1xmu{= zTU~wUgyZp^?!IuhN1OPk7uyRsn_$|WU-rKB;Jfm6}$KlSbu55r;e%I zTs~f|TztB+cnK-<$6F*JHVffRIxb#wIFr?-#56_88c;&H%IW-X$IO_TiDUb!FYrOB z*1Y30OhI}53pzd1dFnxjjki#Kp?4>lZSwMu-}>)VFiv{6YPMxMnZ8?c$KIRJ`eZbj z)ul=}XyV zWu1VmFb%oUhM^BQcuPQ}A@!!WP}gLIg-_k!qgX?G1O0u%oy-nMUD(xtazk799wUZ7Z3v*62m@GZHs{)QhQvFz5aXVb!2Mge9DfsTB@b?8+fk4y zU(;h2Bp-kFZ^?Twc@~P7W|rL?W+}>nYNrr3Q+tPx6*KqVPPId1Msx`_O23AXZC2zm z@0KYGaRuo;CR#TB&_T7581ryAigj``O?Ga+0$|%ZO&NQ8yzJxIV7@+3hcN-cD{U}d z?S3=y9YC|qbNrT3t+&4uZaJh;UOcP^+d8cj9AZ-iT=*`ifq9#S)c$LrQWar)*=7XQ6tsm8}Ib{(#Ot{(_b5 z0R5U0iY$$)ocE7Er)t4XDh=@lxY8pZg>BzCJ@TQdM~)sO?>CP*!lAA~K*kDBP!EbO zUkmjk)9GIBe|-FDyGpaFj*Y0MO8;V5kSg`sza`teYF;?CH`lG}M9Y;K)CPg^C#<^J zo{u7%;;)r8@hbGWf)q8KY)P3C9klkO?%&(;XRfnwFN03X=?;#K-2(GyxxSRiDTp4Zg8@b$9H&Z?rwkHwfw$>|S$;wKsErvF{pDT?0XJMf<;%ts!<6S#H z=XRRUHScesC$4s|aNLT;$i$=L?;gcKz)f$=eq<)XQM4Kj5ErJ7)Xu9|sHE$rVPnELs^+(!b=@AxTN9hewP_pf6F2{j)Y1ei7j0}pj?^(;+p zx)Bbpj4*Ly#nd3C-o(_~F&LHNrCQ`U4zDv*TugUcM#z{%7#0pLHUry<{6KBdTWNYA zVa6G$a}fbwVprnrKw6$fN;zJ<{hYJT@19fvUcri2V>)QXHDDMzIM{W9P_$9r_WjE^lQ9k+}V%woG_rgqP@K*j`8#Q zc1I6#<2r@Y*UfJMc#j4_&WP7Ztm*;k&Vl?T;yBoGpb^Wr90gstWlHdyA?MjC!G8-m zw@(f33OUbD4L*Y3qk~@ptXUdrTr7|Xbvy2IW^;o`i z{*6EoU)8xg5d2J$^L*f9)bIYARs`HnP7dB0a&}D)-Ww_&n-bg{Dt>JW?|*r8@Y^Bh z3vXlW3vYYp=R(E*P{#XjlubkI&N8M1Nu&Wu!138`jGrXzziC<^II5J|0QcWi9eDSh zCBf0jfxnh~8}{wNNx_>Z7e6zJkeB2hK!sudP16I%UJ%?9a6TMF(sUq*Z&7`b_`D=6 z)s$s5n4}8?{}A9)$%%;Pwdx$XzWapWc)&R&h}~)zA!dE>D@D#H0>M88obLn@l48p2 zOC0AHfgnEMW#vDX-{bsn@VX-Bs}l9oz%<-5LA4?k7+m_$1w%JR^;5pq74=WzpvuouHq}V24;LM=5``cH%=;kEjaBflQ6xV)rZ=^ zsV4{jTI9Sp_~Rny(%`QG&gTQc`-+@f0zn*Hje5ZsIo~8|FB3Uv91vPZpAftRmD}fB zL^}Her@S7xwdnUpIWLq1_aEiFIw|;@qnxKF2LbtEY4EY5oa9jv+!Mij6tmOXgU^*X zKMe$bUgBI=6nv_Lk?%MmxQ#mV*?`j={A9q{h}t6t>qo_$3h;kfCDn9JFD){n>ge;rMo zfqk4WBr0V_{BfD*m${?d$-lOqo_4-*Awl>t?{8tYYsFG5 zwqXYB&*)?xk?x5rV&xcDJR8M&SSd5{O*eS-OOtc3=Bd7^;@*YMn&G_Y=}3j|Qf;WF zAD-PnhVMtAw0xyNuwJY_^&&4O*gYcofrH24BS%1GxwAf|k6e#Q#O_|1$LwvFiN<(m zS6@5KzU04K@v^_ztW3+h6WFBiakv4eako+R9(;EvD2t?La5-XROgY*4+`t3atxzBO^iFJB!jxcWBu&qY>m^V}=dLh>5vhG4g0236ZEoruH~n`IU9Fq$ zMg3vJ(C3&sS?8v%0{BYw(O1DcnC0$Tpv0KGvUKNya%XAXGl^!|pM|Ya_kp!oU@WT- z#Fa)~i!4e`Gl7;G0(hRTK#e~ zuuHE4q?>?m3GjiPci?0HiFUbd2CgyS+74Vzz|{;~O?$Cc-v5&|(NNbIf1c#dAv!)- zbL%fhO@^wm&7=AmsZeaB;+q2N%JF@Ki5uimFWn$P9nfS#(?A9!qS%o!vIZ44{yF*s zNEb!Aze74zw9vHkk&eaM9U9@LE%{)kC!gpK_o&MLxD0~1eK+a7^^BL}v94YbO^!ta z7@O3GWlccZ1?XG`R@=(eH}Ym0{|3%xW-2hc_Zl5?>-M>!#XH??dxB8<@f&Szb)03h z`Zmn%ZC~BlH#gGT+ua*Mqb#Y_kIpL}mm-Tlg`op!gGiS}LIkUE|U!H5LHAJ1qg04c& zhhDsFg@Z;khTR6GsJ-hh8bUQT8&jNowx~l;XLtz*KOaK{Q zPZ`@~ZScyvuxv2U4T^yHN*bA3yf=UtcS6i!%7EpAKPTZE@GXQFp|v;F^>jK|THtoiA+im9-9)Y%1PDFlh~HT5xtHupQl?VU&xroI2XPoeha`cj&kVFYZE# zV;h^V=p31=+tu#PJ;ai|Yn-i4~o zwiY$n1=j8B9-S|2c(BU*KEQYm4Z{f*UfphicN1(u)ezcGPtlW8%Blc?QBbX=LABqL zgi&?wkm9*#D7dL)OL4kL?2kF4>RnWVg8I6UI<%~8Yf;S7K3SbifA}2kk`r~moV@=s`2pN@I7f$a7#(L7{d~!W3S|) zWy^eVS!+?VEpxOU>R1h2VTLgN74EE#Ed;Dd$DcQ`46sN3GPI!#D3x6XC=7AmsPUZb z(diko%RpN9=roN5Xu&dUqJ12i#7Z70D6bN1s$-ake^sVjTI_48bQ%ngRGNY|L+X2B z*&TtRKjf|MB~!z|H=yxFJ(_pq*bA1A-vwj1_(0`@O_K7#BG44tqNH62BmdI}DIe?g z3$XPJ)%-zUw5jOB=!+VrE`%|V^6U8hXj3Ai1MB;evex1k@|(_+U$hvB7wg{ngC?Kj z)FWw$&UYv8@z1=v-HFaFrXEEp=c2f>6kdyirBLn3)i?$$9NN|_p{-e5)}(E=GBNCo zDEC2~k6wq8LzA{Js;}Cr zXh-Zp+#8vsiy-?guQr)_M&D0-f_rr>o1gQs=+XurotW=UJvLypl!l>=IPECif$gGd81CLI>~LD>?`N%H0yKh zK|X&)?19wFL-`9Ken}WOw`iQtd2}cGW_Rc`_oEIAmQ{R0P4|xC;!D}@96%pRs2B({ zJbk#M*lvu}(|dH9_cNhe7KZZ$>%fP?0qDTvANJ=e)wf$ z2KwX-KgK`dd~Jwy!*(`E{I^H2hyQoW2*ssmM<}4~qh2 z)AKuMgukTWW#J^nkQM8i` zKh^NZwf&@RGoe^c2{ljZdLGI{JU!Yz(Vj5es^Pb2INLkJ3E+e2uhVUu?U7+f_?xaC ztl!NVexe2AZCc>R`JkBS)#?43`#GJy#+O6+AJZqCk^M!D$CSrpyH&$YeH-}KH2hox z(ARGXoF~!ehVN@Q+p8UZsNrE7i2Lmt?&tqO4X^j5e@MfdeDI$Oe7e)^gFm6+?Az_| zl!g!4K-}|FtBy0>N%`O}X!z%R@IPw!7kuznHT*Uoe7}Z&%?A&PzD{@eIV?L&)$o6@ zfw-Th;X8fs<23wdKKQ#d{3##&JsLhq>#K>b*6=Al_*ojR&5TLz%+YY^V?3zioFnk# zym=iHRj<=uWB~fQNT;{kC!=CI{WpE-RXab<8S}yA3J|Fxe8vkA&e6_|O@|E|a z0zcK4+HraWUOfT6Uf?jUzK|9;jFvC_r@;NvbeuN?u8pddr&;WmD3JwqoG$`C!|H#v zd2;R%c((tQ`8ec!1aN;FCBh8g)19+C+*SH+e}VLqK`0-d69J#$&6YThp*u_9$9nh| z>APqFJWU1QHxz(>xd8k=z-L%~g0b6Wu-DUzveppAl<;1_^OdW%0Q}ej@Or@W(WSpQ zLrzy#^ggNS(nrh(e^lUE|73wq|6cfUGvs7u1u*bIz~659X`F{-cpC70kh7zBgnicuIH8I=YIrnrXSNym3|V#J`L~DE`e#umjEx* zk7~H#^9u#wztZrnI{h08y~FqE^65ie0r)=xPP$ulg`4@v*ED>OhBs;aGpFUpzo-Db z8F1pSIbI=X(0Dc%NRM^ey!?Mp;~CU=x-_1rHGH3jbKa8S%(v&q^M1g|j~t4QbexZ5 zxS~M%ew{wdffs_zFXH-78eV^zfE~^gGVCaTCsP1UlS+PCt5kl2=S&SBtX6Q&;V~=) z991(RTwDM?TmXK9#y|F6g^Kfb4BHB%f3yJn=wlTBV}IO*=wcsPH

pt#FMSW-Opnc85;*t5@T7(hHmGOLjW9Hx zpwc&;ubz!x`Go@TKLb8pW*${~G#?35F~G{l&(;EV+wAt@D^06++Ue4<2 z?e3XbLySwjMdsRhjovF%7sxA4eYG|5zMi$6i8wZtMV2o)4Du1s!;ZIu5~|gNdfE~l z|GkuE$FZ6n$DX)2*0}o)lh*R)T1s+4$!fA9tkbvMy|we>YJokT-N_eOUOP*c_K_;l zK9D$MWb^rU9ba^dtUu&DwRKYF+|7cA&B45h3}ki462Khp3W#(zLGzlHtn-*XK-%TY znrq_o8rQ9E=)Jrzt2y~|^tP{BAGz$nXlu_llE+q_)!kidue79=7wz15TR$`~E8D!O z*e=@Fq7ve(FFRP;##y3cHSs*{D_h0`H^cdHyuaAj9_NdTR9jet!&F>PUI!#Nt5y@# z`%M2QXx736pke+^rE9K>7iyUkjoaDEAOmRe5b*?YmkyH?lqtxN+`bs!k5BJXXQbW0xLp>F-(F9`9V&vo;!AR~tG1 zaQfipxo`loSkG2l9HWPYsx6v>Eo(-UtmbU!XhYYOQ$#JrSp+YRU9g}b7QbN8qUK0T zyrp45ED}fJmJ9ttj{v%QZPWU7MAe+AYadwEE{1QR;F(xSwfU*m0E~_OYumdNqPOZl z)$x6H^d5=!$ntey2JgAIt-2zRbmA+zHgtBa zjxTTT>&Gh&d8pU^cd4wuQu3*_uAw8W)|&DW#;SSEs&}3O9Rcr{b+qKIJN0wcpqlB@ zQzM~(v*s8D#ODg)-D~2#ZC#hQ$JeY&Ak9@!+EoK>c!OkZ_bTXd+-pk6+Yow5 z7lqdMYkJ#rvmIpEa0I;h!<5Y2URyM5iCx`jl_)oSCK`f7w`#MqYVwIpHCkJs`o*7k zTkqv)Q(Y9O*AyW=+cIfkUDkeiXIC5)S9NFz{9yBZumRT`af0ZB{;k6k3h=FK7R_B- zUtj0tbTFIe&^8~Wu8-Jw>fof5aQG_Ikr0k&nQN^59xT7IwfX&iUs>}q za45q@N5+s5*}lC5ySE$UEwLQtYE{o9eG=+4ci4G&WBNnlzBZodmKW3TaTL|-oV^Nt zWLNk6)&0HF9HVQS6LaM@8e3Z%!GBFnO$6bP@Xs;>VUFd|$(zwM$Ho_RCOTTw$6IpX zhlF^xhnSk?!@L5ZLf9F1C={$A{GlX2q|v)7W^;SO6g#)w=2fulKwjlu1S?O5JjI^V zx1p^kBBOG;#bC3!6;ub8?R9s0XN92 zLybIatBVr=xjy7X61kHFIeh0NR`Ud^gp1n~%iDX_!c($JS?g~hHKIx?A$vZ#yWp~q zLa}l)T2jq7)1YYPlq)Ac-t7P{b7iC!S%w*<`Ar<ZB0C!!wJG2Q!9wb*T<@XC}MI5H#sX3|%~r@rYnko?>$X1GIvZ zu!TEfjDc$6EwwU1i-8sT-1*YB-g?hHueTjQ540P1%d*WC^-f)9FCkf-H z%i)vQBbx*J#z$_-iZy>2J~FJ^Iy9|zmPSxyRczvf$&sD#Qhp}h>n7(1B6|eqyh!WF zv=2;JEsf{C`WQW52K|xs%P`WtysKZ9AfRAf-M#DD*2X&%?dZn4d!tQA);y1s4LujM zP>;Jdh&CQj2!HcDoGSWT)%V(2_Lu4+>kh6YM|LLf;L7p0PvVirf%Nb2XCn@V_-~kK zXlc&!tfr3W5MqW1T^SXx?Xu4GwX{rqo$w+sSc&r!26#z#O?O1r*|4%Ffy;W8_{SH$ zB-9I3?RWmD1^>Q=GyN$Re4B=IN0goZpY(bm;TKuyzgGbMlLGMNdaIQmPgenW+JawV z(fz6gZ?oY15H$m%?eq&RxShVkg4^jwEV!Ni>lWNj|04@-r+>kM+v%t1Eo9`=PQT8A z+vz`L!R_>4x8QdAA6ala{hu_P)pIKTjh?9el1gqLvEXl7crLZzYQH4++&T_d;|-pp z_5LBkxkbXj-=X0w?-dqY?Tegs6iCyOHo@(pVHP=7JSfJmHOiU{mx_e?9E<9 z>+je9e6qRse&@_NXU@!=dE9&FdeC3#LBG(0zSV>NY7hGBJ?LK*^nA9%me=v-IRKW| z7X)575Elrs(En24tpY!B5H1kha*xOJblLD(0+;QA z&*fQq*)G-#T+(kBxTHT0&o5X$lKxDAOZu4tm-JT)T+;9Iz)v@ho_3IYE)uxpvqIpK z{&s;&`kxw{<#;lFwj7@l^ihF7CvfQ>e&@mG1wp?=@JR~#69vxa?-<@c{yKCBkFtPfujd}MuCFX&}`xL?rA`mjsTOZ(^JIXJ7Qv_GGxv-o11*z&ql z;H?6GMc}f$*5f%i26uVAC2(o~Bk;VNrI+?UQ{a;R5`jzlP7nNV2B-a{-S!H4X}A2( zXnja8{m&qSGhb2stbGCkm*sw>;3NG&P~fuMPZWG)xsMX`QlHU+Uiz`u1l}(6JPOb2 zF}T~yDF$bLPr=XTt483X1b(I9bA!M;1b&gg69Sj^c~9Us3i>H{9*}{0O8j1dcOcH{ z`6q#|6!_l+F69>EIY9Uq3* zF^qb~1pk==m-@^#IQ1zN^!EvRsn1q{OMRXe_)UV(?*)Fnz~2|R)W6|45Ft>1$^RM; z{1y-VJ`a4^aK&EnZ^WrBua`XV1)ukz_rNC?XQ#i#1OI~ue%$fd`Bc&=AkaS2Zp{Mk zL7dH3hrqui@aF_B>%-p-&iYU$=#Qq;L7?0l@U#5K3tX00mB6K*9Rinn-Xn0S=j#HO zdiEbdZS?p`>Up}s-FjXmaH(gbz@?t|3S8>BRp4tN-`e5ilaxBjK2p!K3{HEN3;Gs8 zFYUQn;8M@82z;&J^G$(E{gVQh`v1$|)MvEdlgmy5fp)tMKbx;l3;Y&=2L=8WfuAby zTLr#c;L<*;4eqXQTLitd&mRRY?emhrr5$cE?+Y+r(hm0;oN~tqJ-;XDr5&CUxU|DA zflE8QByeemg94ZOU&T%o!L4VT!Kvq1{A{_z1ijSrCjysteopX_?In+$G=jUmlnGq& z2^-v9Udsi&EU&c!m-)R*;Ih0b*(n-*WO>yXoN~wEXYDXu&`Uea6Syp|R)Nd%x>4Y= zyuK%JX@@5a?zY3f1--Pxr%IVCZD(nRV+Ahl5El6DNN4kVfxy2i@OuP)oxnE=e6_$I z5_pfmAN9a5qw_*wJ-GuvtIuA6OZ(*0xge0f5kE_RlE5W>wZJ9)RRWjvT>_W%_X}Lo zKP7NU|96A49OXEFpP-lH{QUx#<@J^apLYels10M|jZRCGb1J z%i8}t0>4?{I|VN5=PMrgAUY!i>M7|@^uWgoJb^S;pE`j{doDCM?K57`>rcaIx!)G} zPXv91z_$wilKxi$mvT>}b3tIf5tDvkgxDD+PUxpqKj05%@)d z{^x?vX##&v&`UnA3tZO!LK+2uddm8Lh6mnZaO!_Lem1{xK`-TQ5%ebt`W*u2P|oss zTJV`K@aF~n83O;kppOdtFCO$S3Hl|1{$+v7a>q02Dlori3i{~=r`=?JXAAsWg8ml4 z=PZGLRnSX5-xBnE#m(C1yB_pg1ioI-|HcFVo4{p$UuI=OV7|VKpOqVDVIvT~3O|b< z#=<}#F6-4v0+;pba)IB6G?vfR0+;ptDjFSu{3ZQO0+;l^7P!>sp8}WqjACIRxcQ$e zaLK>f;H(d_KDP>7*5|7QA6cKT7x-o1W$mz1@R9vaT+m-G=+_B)sn5Lvm->8H@R9mF zBrwNG^j7YM{9eRGIt zU;H~E1^1!{7Dad?W8oH<%0h+0&fxc zzpK;qQtoFbXU9(!_ydB^*#>7l4+;DNK`;GPv%s$r^fw7y>T{0={-_83ya#@I4TK?3 zPZp82f5_mhCsNND0>4(!w+dYPho5-hc@7dIkiVp#?t!oOz=P)~Hj0m|SBnI`5hONW zF@Z~Z11mf92Yo0<^Sy6?KGHtK>C+j0=)(W5VIMa3SHaSsVEAB5{|wyj?}P3AGw{0% zeQO{Sf8XFYy6{mHgmAM9Uu^K(UHByizt@F-+Td>w)%;j4SL0{xHuEzYCmHRq+0b9* z!tH*$*Bji;=PN~-e6GvW*M}dekEHL!&&nMs@GgO0Ch%1PUm);qfq&oNR{uMV+`TUR zZG*pk6cQs?dhS=oFzB=TXz^DK{%IH9-#q6rr6iO7vj$&a*1Ig9GYtN~NtyJQ8vG|N ze6hh_KRJ{Be++)mh2LxN`$lEb+vik%=)!YZ$Ov0q_+Jd4r(L+^bF!J|+I$^h_>6bq zV+`J8aGLSU_}Owl-OP_HpNOHaapBVqKGlVP#o+ZW{6>S%ci~qV{O4l6L+JYkzeS9P z37u!!ZC)iV5Uid*Hux|XzR}>DT==j2I{i~({6PNA12z6T4}6~s?=7Q67P4DQrq^0~?2BhSgiiw5iK3Kw2z@G2Mnn&D&2 zr=BI50T#BNm}S2{;}cVi3a=CNAC-@(CVlu!(AnrC*E9=-&lGY^_oZ+-uD(t1k^RwK z24}gPAo$!Z=r0%eCV^im@TUZRk--1%fxqv8+y08}^a{bp?$<+H%C-F-aVhsK(~r6F z^99~4_|Gsn?LeE`a&H#&vjrX#xRl#t@RLA$v7qNCL>P!Kz|Zox{V?@hDDcf5{Qu^G zzasEOg3lWQm-e*%u-iVxGynqeW%yY=M+m%4;L`;z?O?|RZvJr(dVH)(1=3$3_-_)p z^_(I2NIjPeT=Ks` z;Ih180+;3fkiaFMM+7eUJSlL==kEfSeC)WA(URVdD~U_`Bh0vrctY6IjyK(W#t3@J z$BskY^wT`(?fBG9f0dxWRp?{Kt#1081ijS9j%VHUUl;U}za8hg>9+`a$^WMUmv*+} zT=J3hZwq>f+wrnn?obZq5Qs}XM+#i>vEyPlpNWEA@;O)FQhz%xCZ9R@*>-n@pqKT; zj(^>9?Rc5E)YFcy-S`87zodUm;L<SK48R;J-}h`5S@DeEr$r%ooSNwjRDL=w-g%61dFQ5oVr5 zePq6lHn=-q!v!w$b&}vC^A#4jhka_X}Lw z)6T!iN7}P+PF6NpI) zi=Xum4+(l{=U)q4%Kd}EPXZ4)zx%7erJeT)J{fj4>k#g8wCfhc=LkJVd(cNb@Yx=C zv%qC}-6C+A-#Y~^%f+q-)1LhO!O)GsIRou813zo$+X*5tUvgdIL4|6(5$BfvNrRL3 z2?GC_!O2IiOFS)bxxVoyf!E>O^8dTQr5%a|F70rZ2Tq<0)JM{{Dzq=l>P?iVT!=7 z67*6Zkf=a0Yzu|@(r3C;AMqa;j0YP}{2_r$dioz5Bt89=4MpaJd@ivdjzM!ye5yH@ z2e(`sM|_sOz%gjfnQc>*6k^voiPLsA)S46G`4+^HKAeHL8G5VhZ>#?rZ!qUPY&Pel zw>Ba4q`*y=sE~gN+_-9m*zakQk8y1ZAH$3y5a-W^GX*}xf;e^yoch~(_9cOnp1zV{ zgTTq(?vMYFz)9aiVg{oG59DvZKQvI_q~C!bgI(WaG=FxVdiB9cgAup;(XVy!`2*q@ zb_<+xQ{OidIOY0{-u+A^U^IVrU;82#Zuhkx>B8;)^ncAa{fz{%f!FX}RZlmFF*e^lU)Xy_jlIQiS}PTBP+ ztN&LGz5TAG)!*)qUu+t=#X~1jINn7PIOTrR@M#t}zBIO(4@^rd8qKw6$Q7`jSq<0)$?H6SDjA%XJd+A8hCg&3xJNx8MCbQsAT? zZRquPfpmTe+5P>`bkUz~=&uzxRkPm@>k>HmPc!uE1Ww3)C!p7b+wTPYRN&;(VE7c8 zPJ)+|Yrju+q`*mkr=cGsa6&DnLpxL8r2m$opDpmCf!lD0z&~q29Pbf0`Fz*#*)4EF z4d%upzuUt=jP&+-3HvQb;>?%*UYebETl!v}AXJ!6k(d10?+R22ocwnhdOI)XCHdR$ zuB~;^zij9?3Y?Jr4#AHEPX0mTH|@OD^0(h_`?rg}#L(M$tKtnlHe6~tb>jTl?-n!( zoO12Akv9mO`D!up>^zY?NZ)AW?iBQlv)?)Rt-wh?&(If}!31f^-+oW-6oHf8KCi-W zb25;gKl{Ce3j|L3wTAy40w;g_UAlV&PWlaozE|MnZ@;_X8`ZjeLA3D-9I=n5I?lCc zi<_DoM{y&4->BJ*(MI2>D_UE93K*5K2~*}S#n@5TBqLR7ryQ|i@ds0IbKy~o7f~X0 zDjS7Or_(H#ENq@PzX`j|39PS}kG}`cs4Hh-uVJ+>ay`t^+>|8~{{i=OMx@eN_Q6#< zGH2%T6W)!NJGN!m89RR~_1qS4nfjC25XuwS-)sdOz=?H~0KRTNDy+m^~%>9Xv3ZY%k$)@T_vr&o16#hu$2 zH;=OWfV(#z{y^evn}*}_1q-;Nv2WDEMbV~FHRnw~MeS{FuCdMAD1IiUX>rtgu%@~7 za~3yZXYo;2E?%@GHQj@Y{*MChf0{=xGx&tel^IT`cSbn>H^cqZ(JQ@U(jQ@tr{j3u zM@VnikL-E{)1Utl(l>*afqfeT({oMXQ1x$PA_STK0$d+z`c9QPGyR1hA$?Dl^xyah z>F>yr{=Scpe!WRA%b)8`hnoM5S>$sa>QK{f&XS&MB8QrON0#(lFFn-szsi!nAxruo zFiyERwvu7Dxk#KN(e!$qML7qcP9Y1J<87t@cb=dD*53mBYDx_u^Vq5-Ppx-az_zK7 zOa9!gn!+)JwBO6kvQvRp#bIZRK80DAa zCxbttuiW3Ewdt9L;XC*#{nb_aUlA9Z5jGCiNBiHUrtcm^WUGILPv{i4nG92Z);TNR z*1t!AWwYPTI-P#6KBWG;?e`@oxEr5Kj4aozsx}ery)E0~Rnx z(#p_k()V{6W~WJiqa?<;TmOYfpH2U{zt#yGOnU17IsDxEf5St5gIR>1VA9)zTmIKP zc885T>s8ie%;UOB+`DxfZXy|82Rq@ zo8O?*e@Y)x|E=BZakq#5L6d${Dy2Sg>;HQX{U0{^_nP#y---CS_2>FIb#d$ed;>rP zwmBKB-uCzhliqFr{S7*m{eGn7;nx2*NT1FAZNJee3>!6mI|-0m|0PIFUEJ-zVU|um zOdnGJ-STfU>FqkGC2TY4t)H{Fm2ac{aWK`X3(p zw;TPfp46Xp)TX!i=P`d_`Mb-n=YmPP)UEb`Ac@=FmXL#dI^HYbB^ zH}+U((n}hiUu4p|ahuNK+&7p@*>D3Hq0XcaW}s>NS-9QEcc*VN>36xt57z!R{WVD6 z4;LW|nBzuczg{x{WcgL%XVcsAV}9JS$)dJ}|Iy!?$>|w5=ls*1p6v7RbBBf-G&1Js z%ow-*$Ua;8mcQt9Uqb$5aHl_YOj%HdQgNZr_5X2W#;BNa#HM^b8~W1 z;&Xgwsk$uX@%*+I(nv_1hJ1B+x`euX7C#<2w&1fB?Bj#@5jzgQ;pV&;$K&y{^99z+ z5)(H9NBY*2%{Au)OizC`%AAkIahy3Hk7I>7XPKU6&RNDRC#F3Uzq8Ew**J#Gd6hX% zz;U8EpJa~JIC8wr^fmZ7<~(eU=ita;IpaAz<`9?XTvy=Q!0Uly-mbc+YDU%cs%hca zyVIS-clnshR44YD6MNn1c)4V@6YqCCv+cwt6&IED?!l?At2fYfJ_;e87m3d=tcvw3 z4kr*B28llsZ}mGsBC-9EoITNjQ@Y*^bPfe^B-UJ9818s45Li{6i$@u&rN-FFvsZ+$*ZUw1n(KWM2yQrBubA!T---DEQw4uA2W;!9V20 zk#0&NTHHV>6-o+3)J=&+i)$nCYic9014ieAfzBnFJcp)q{*2~1fI2rojc{UfaXYL+ zrgfHSTR3)YaSNEXWH23>$@JQkqQ3(J7y?W?Q_R}H>{MU8qX%&)k*Zi{F;4@VI@XXH zlsr1|j@xhuBbG;8t}~ZBg7{t>NX(-uKH8yZwsW#B*g)6y(8B3>q9hXAJ@c}vORFxc zy1c4>Yv9i);GInCysbV27LXp_sNbF4&8Huw7A*M7r zPVr8s_lxwXe(Cf@+0y%yYu}^B>GUlkeM>sME(^uteW?P9HhBqDNacp=_DLe~>*r1)*)+8Ap z%6ZS6bYZ|A(jP<;-=;5^?!?}U#9o3+pg$;58nXSL*PpfGA43n^?!ha}hrmiQ81-yL4iLZ6IpHu?2}i(4!jI_K7Vfeap6qZAjnKO?L6P zIwWpUM9j!piczX>Q%)_SBcjH zi>8(ahSruQm!Wda>50T2DPrlwA38t|KYH@Km!II@l-T>>5pPb3y%FB>US7E4U=Es$ z)A8&<=j1LvzT|8<(9a1xu*FFnQ45v!GwsP;yLqrNIXj&ne{ENce?C=%-k|Z~k0{0C zYj=Sbj?@`(z==JBM0w7LEl$V3a{?Ny!ez?u|$EkjPUHm~5`^T+* zUCi^xt$r=Y5EB3A)i0nJ{ggBAqz|e-aWId1!`k8fsp>=i1LBxANcMY!F>XOS< z{S9|KnR{+x(kZB7j|WzbVlj0+`!EhZCs6%(BynwSE^FDAJ!b^EE(3f{;*9qYX}ZnW zHZ$@#5E-$@d1g-}F>)JfnrsowY2`nogPjrojKp5k?JVBK+H31_V$73ijvSCgVvnf! zSQqPhI9~00y((~BweS4IwPR7|?>_((G)hXoHQavKY#VGw3E}tvrWoL)VhkLk5OE%tP{@svA`HY=u2r+LAZB?ca*_-%^@W>WwC#o@uydGt+Ezo(1AGS%% zC<@1?6gri^33M((mlcjJ^@rmCW9MNgF@?t>rwhY9zHS$et?-9q(~H8}s|#Tsq@G^7 zZGf*TUR@H7FDVVjuPd#JO~z=nI;13YGEHJe2#hL}guhT^Y$+%K#?Gri1dkyl;nym1 zMJXhNAfZA_sBVCiT1bI}T1co>oEtDh1?L7ZK#vC9n>A-p0F0eihX@`U6z6))xfYxo zz`0Iy?rPUrXwl4Dz`R8^ z-8>(0X>lo>!FA8f2|PcoxP%e6a%{nqn>o6u>R!o2;dmEoE=m6y7%{sz6d3mJaBL+p zM6EMX?}5vu#X&?6p}>39ap-rcM_|}qvVEwRSocask>9J~_{v>`0z)@33OqR8+`e)r zE*Nmicm~8pii<$GayPDc+;5IbS6o}=?`LWde3wThcDDvL@krnilk`y@3EXdZe3wU+ zgv_WGkKvg1Cy6T5)wSfRyb|YJAi0NJar~NLnlQZ9GO1I!KhVMZ52^7thV_wn#gxRi z*CBdNPdNTqH=@IdFVmo?7}3ITe3d#+wB+mo1}k4bH=MYLwI+I4I9}7{h!_VktgkAj zPR0ZggW;WyDHSnitBS=<1T^~_wlhmpD&GxsErgSBI{Fo(^XOOXoQ@em;Kp-^#UsTf zPGtnwooBPRu0&Y}I>#alfn(dLq!a%M^Wh}aK*_1x9>q9RpIdF6_%#^OO+-GnM-Ox2 zI1w=zC{9E^bWA!QNP}VOMC3!qr1Rm#PgnVvY4hRSuvHb!=~S(&nOdLTTwE0Byd8RB z5@qH;@kg|ts!|(0^?6#G70|QV>WLE(13jy)o;VRP(6id=i4zgSmTcUvbHkHbNov-$ zh(6hLXNfw$1J(UYOVqqB!U@KstRvz0*{Yn_O?LF6OGgjpRBm1N$2oyT(=bPmcLiCK z0pYl_X%T?rO&g;uAcE3Bdxmgq zlcJ96*x1dCh0jy8v732NMY=`NKB8!2YgO#_F48h+ueT9baBe(cR9%-IPH$D+jp}O9 ztHjvtRC9Woy#3#5{uvgvy&XNQyS85nEP0sfq}wSjvsQf?5y`VLun8w7qpTkfbY84% zGQ9$$n&@Yoj<;c^=WLA($ETw|x(?mXjEZgjK7*{TC{0G82b&KnW)2wrm)cGASXHM) z?ob_DP#u%aD(^Dih?&8d3lW2Sx2Swyz%q(YZxi``4TYh&5@*+ke7Bi=wv6bcTv3l4Ci*TGrOC^Q`W z$=lx|hn1{UiUI3>IKEa@P?X*0blJU)A~$(ZQQ^3*;IUgtsF|sXnVd&!muM9 zgMmzpBOQ1c#GQyZ2jf?)RUND?M6->S#;b>@COd>pwrdKjxFsk`o`uVYQI-ey2ewrE zlPAc_A^U#GZkx@mdw{Ww$$s)()Ko-PhZv(Pb@Bw{iV4_E6s*cTIlz!MC`^@Pvdmyb z3R7j1tY_tqy_L*;Qy-6{rno+eS;FyK2A_mukDPB{JgQetV{c4#;wyJUyb~{UVxMv1 zmDr zFLJ2e7OGPm=7I#>3|2?Tq04X}+D1C8fjBK(Cq>Cvmmlfuud;u)UoJhAJ>!k7HnyTK!-uMpbpMDb}kY zYW-tdJ6S^V*LLGTMK-ESvmDaCMO~=@0V5yAC6_}rtQR8^J$mKpxj^SvNd132Zm`{P zylXe4&vfi^4X>~$i#6G;dfnCO*i&If3IF?}nUq$K&SM*8v=6qPn13t7$#2 zu{_4s${BsH6H_^X?!4TK>FASYva0fuTK~m-bR7#1qky|rg-Gs2JC^NhQC%A%FD-6M zE=LULYwK{029P{aUptteo?n`L8V#27{q+3|EL@^V#@=ymc$p$1@y8ylZrlRX%8sMbBCsoXSNdJD!Zanp!rCcQDGS>>z#gZ)$wx#5FUqsLs3D z&J9mcv=|Q`Phe~+crN)K>=sGCAhUxprb(oRiP>SE)2`I@cc)jvrB6UDivYVV3CR ziG^4UtP0%KJ9AR3SJj<#<*@73>AP1QFQYqtD>^=s*i@p7_W%#52UwM`HxZn4QNkih zH@jwmu^U!n*xYyvWUE|!=+W&@)-T>Kn-qIeEsV#WcVa(7Me{>kMh!avimP6d;;)crkC4tVz;WT-7A><^k@f!<9a21EQ zkIgS39q*9@$-36rjx$Jt^J8)IhM5|d;|v`&iW?{DMp_YXq$wyEsr+}KlkcdL9$kHe z(`8120y7D;IQA@xkwcQX5!}!UQIbPRP`?)bxfxL9u0ZEcl#fRoNs*E~aMt1^HW){L zY4KbpZqaH|Bm^nSxwiFyCzjJI_XIlmjvAS$C^jKppcB$Jz#Mc!Y|Bn;gYHzeYuvJK z2`BWuFy0gECLt28$8np6Ap^H4cA^j@0Xg!T;+Q~nBBld!Ac5Bmr0KyCBs*{bPo{(| zw2Z)}&f;J28eNR;q&kat;+n}6OQhRnu9=YZe>U`6aIEfpb4d$t=*)F0Ux*ecdy45B z${siAmadd33oIRxrIAo1w|Fl@JN#g;ePKC#e0wjdEcyz<)A%s!akJ7 zpr%6FW^LBW*8-gvz#)+0Hc62@9VqXwvAQ~^Pb~~|oeFo&iF?T54dSk61D$6Oi9PM0 znqf{f#7ZB68Vm&Ym^zOFg<5&E^JUvoyLy*g%?6ZH^(ZprOFjwjZ`OtH&>ehW=p9NW zHQGzfx6yy-@r&w1+LGUa0Fw@b9DosYEE5&j4!)SjE<#NOj)sx^3xp^OnT1O2AGX5F zDOag4w`X3aVukYjv>xS=`@u5|7gL=$`S0m-nFc3nfr50YDEl^afaWuAY1FW?$Z?Rg0gS zgT8+^RCFrUt)xKb(F55>Jy_fTw#u_=j|#G0l65?ya>P{vc!sVW#Bg4V**n%CF%yWy zSKx_(1%y1R{I{Ekx`>_W^%r!NN#P3%VW@J1_k?q8!aoh0x zx-jm|sVt9#cvF64#4HDyR ze&DBCm{2*GVmTOVU=FM(I?;M=RgoKtse1$+BF8*V;*R27>iWUr1H6U{ZQv0-4UY`E zH}5Q-i)(g(eiF5qi$a{Y;hfLMh;E%E?SNG`I)xcl)&WZCyeK6JZO!gekwPS|wMYye z5Wf!B=-HS?Nx-BNT{@Ga9iyWI9$`oQ91NL4nJ-?T>qlQl5=bDa>OOI_5?p=%4n(mV zC50MIG#Rd{``-naTvhkK6BkbGj$$SuS6*ALS-br`AOLSNfyVm-o&Oj@cIk1!0mR5| ze?P7xu?LGyLPn!-HW#MkrK(w;;@gtS zmhREQqj1c&-!rWktz2pLNw((kP?QZJ$mT`vyhyd?lwTJ?etY}F~(^)XxbTdD<` z!9B}9vvp%|ud2((ZRKik-+BnG+|`;tsTQ0(Ej@ljueOm5qf*@!33OhndIGdP474y@ zQauDqQ(bo`A`G$C6bBiPx?>--wLJl!O`)|^PrwbfdBL85n*h0c0*(#HhYQsEFizuq z4s~aZLV1C@v!x70w%&h>q2B^_+W&M-e@D-$b|ES?;tzCn4uXa^Jh2C8<{U0HzfsR+exGC*(G(`% z{THV@g?@Elh|BbiVjJw7`>gUp@i6LDEA7> z@tT1qH!e4|Uf5Y&K?Y#dU{WcUjY6v9H4BMOnzhYbx8NF=4lWVfW@2k`8L2!F=)502 zo)cfK*l~U9aV{&+1M&h-3^YIPa4LEpTUY!+?RmjRNyQAloBU#{HO{VKED2+qiz&cC zU$4|erl_9ER06BjILF;RXw^b0WSzYdxUMtWwGpn|5$Jpj+F9?52_xhpCPIZ0cwB;Y zQ*~iTN)CY$dTgZnDDK%qMHDY;N}f=J92g>K9P4ooMQEWMUw-AHv29kkyOZl9|sPW z{(vJU6dYsGB)fS;bfLN4W3G#EB>6vZ1UZLTTpB!{X(%rOcZNGGe2~D7Y9eSeaEXMF z(c4Ib`}{{U#d;>sAWqS>Db7#pu}U{DEI)G5RrsKeZE(ky5@2Dx|GHUPNd60F>G?cM zu$yK(eL9vnF- zd_QyBj?=V$bl!rW8Vrm4?m%=lexyszd95$M_Dp`fl=`u&$;OX#$%&cyZLm|%_>0hs|x9Y`L>K*RjrlS$gHDe!l+!a4ZYzWBJ2}|&gG|(}8soFk zky;KP5piNqsa9`97#iy#)*J2{Ss9T7dU?y8ULjeI92k3mKfN3Cai->9hD+*Sp>pEj z6%?HlW}-r6hf1kL$cb{hanLY3bfjE#ql2{qAEv+`LZI#M(Tl@UC?`CGlRO-^tyAlx z9OP; z>O24QQBf>$QSvfXZ7?p;cmJybTWb8tvjMt$st9}s3(EF`5jrDC`jDHa6&BC{8 z4vzuU9gloRB|9o9JkPPfQJCDW8a>};`6ZuB;tMXb4HQhiZO)34KAff2+jk~e70hb< zA)I50{-Y+@nlVsTGE(91+mS0Lfd;@MJcU|7?#6{$o9aEN8(6qHty7Qyo*=s zPQ!>0XL#7AT{UyHAg(yTuV9_xZnbiXHO?T{WA%b~6BoqS8)>~pnp%EWa|5oZT50@9 zija0KWN2xZq@^hikcQ=VbRvrF9mTtGR8k?b78%5_;0|?PEF&{Fb5NPi%p)oGe??U^ z3%2Cs#CDPSF2Pi-h5KM#FmFq(h3k%;83?R>+^mH^-aV~&K%i?>rmU~0WYwo-k)Ik0 zQQ843EpFGt3lW`O59fx*nD8rO!=Od_a-C8a^J`6eFD|<~i@8YzXz5$9+8yY;1=5}P z+HPEPPSeYO`n`w|n0B+zk3ps9Uu)xQRpvp_2^>9hI)tfc(LAH0Wr8OttX4Tub#NO zxE*>$VtC`>&#F$7M{VjZWkYH}9Er~^cBXW_7(LO6pMoU5_7$*SXFb(m?Nl8GZvClc zOujN2*o5W)?cnV;>pL~+^-NVXa;Uu%40+2`Eh+Big^XoQ7R!~r?s&E<#+%hpRfsk{ zX5|BXfv%@nSeOj335!|zujwUkc!DiG65Eq;XBF=~z8L5_3^`#X@+ViQ+mz_{6s|@X zz>AW1sud%P|C|hCyOO;7X~vz3p7+?vr!e_V%ZKA-Okg&vNxT@3s@&@FC`JsJSs{0k z*k9~8j0J&#lD;9GoUXXFg9Iis{AdorPgzIsqj|FNBVF>#YEadV(=P$Od^UW!t=}p# z68k%m>2K#^=wI$~Un zMxOjMF7!Au9OsS3m-uugnoiVVv(e+MX~lY=^;6KMsEU$5)_hXq2-d={0h7Hj?$e9- zA^UzPyLm}?NawIM~@A;(8#T%`u z=fNV0WkvRdA-vCuXLg?BYeSLv6`6P8ojBhAT82B)SL0=(jGpN!G-5i)BHhhyMO)Mz z-^DmnJySJqn(mLpn)voQI#<5EKC2>DjpE1KLU=+J?}B5&f#oOq(>0lFx!12|Tb^PY z;@lm4`M!D(EvmW!jM05oqZ_>jbxC#Wg5{J&{u3VPwKdw+X)_b-if%Y5Z(|IYVlCMB`dVR=Atnkz47p2@&HAb0g-KGdN z3^h8V&nUvf+x}!xS`U>LOw#VH+IlypZ^)9qHrd%XeQ8=g+DJ-%C`d)3>D4caf$qixn-&3!sAZX1Y#@_wi+9#<_A`$E#dll7Tog{z_GzNNhpIej9x}mKw;zzgW(7nVi3p;`{>q zg60gDrBW@R&nhuKYkr;dS*TiqXJIDK{1ngD6i?-|P}at0EuhaTfzO)Xmf^Fkmfr#k zk^j*tju%*trgkhy`z*WH2D}0N41Lz8`R{VLtk?cGT-I9cvcA>}Y48ra_2E!m)thnr0zt9@q}j)MQ~t(maN`di75|Qg;5jdoNDtJlE8D);ah*$ z@yI%Hg^53(K@HKtl=8XJ3Pw7yON&dvCuE#B8e{49>Jl_Uyu@T4mP8WsrXnk0op7-? zLshugxnhW}*1>;39JGGpS(G2ez9(uQ3gAV_mC9pTe7T`7G$V9_cWwlg+rO%OU`(C% zPu9PdX#bkACXO*V{OeVj{tW|&Z2pz$8(eF`OkbPa)i-?>|H|~CEa^*=llrF5;$M-z zs~u(1mj(RmJ&Hw0HKAs_06Fx2)|2FO|B2fzN){-$%TeScbLLO}IE&|X#0DrI=7q&* z(LPU}bv;Kt=6%D<>yeF&^&HN--Rn84{8-P)bK>*;W;tgTmveTr0w?r?xyoOw#Y=** zJ!*jk?>ypxN~GmjE2PJn=jPkd+n3-X@y-($4-PO9D=b$f=q=Rn4I@s7#4r=1&QA51 zAf*MNAEgyiTA{iS(hkd%mVgXBR-B)frZ_+voZ#JhQ3Gq;A#hSsIR@3^(GB`ZL^-;> z3AZ;zW}Zs1=l(3JtH3Q_qZ62Od}W!c<(vipdQ^&5$PCcq>G5-)<(!i;Wqm&-i}(1A zEb^n_(D~e5TxX@}4VJKU8>c(Q@s?g3A*}@zW{k?`5<}gc#ZkpUjZ_08_`m|l+BCKf ziFnzrQ<;Sxe&rrRTY_q#=O*Zd4O{3}Qwu%6gR|Yr*D@OVEcDDo`Rj$AAJFSY;@@P2 z}=sFxSEkBG$Y zRx6(vAV*J-?;GDpdm+P_i&FfS6iwE`)h)Hq)Sk(0cJbUktd7mf>PyIH#`+fPws>Ao z)m5t|*S|^`Zy$RL@j}KHn$+fTyr4Utwh-%E%7xhUuJtX}Fwt%HLQ$-5(eq_r-{Obn zl{-o{tCgQgW_{~rgBK+esyndwpNPlyBnPJZ88y%0b2j8tnEW5hhuwe3L2YODQhlg( zWNMxeVw+=jG0?Y3sTPZ3R zxh_Yyot{T_y{(klm7MJ=zr9MS1Icd+`QWG4ztRH;t|yC?zjjDf0$odBEy+v4hWa4= zp|5Xda53wfo74K}`_vZ$(D~8pYj>eQ`>d~V(}DY;T;CB4rZ`#KoIz?2J+^AFw$@MpX@{ehK#k!n7c{Ol37j>`O-8rX6&<{zTk3rIZw3H|3 z!>CAw$*&-*T+aIv23=g^&;+*E z6!N&OrpULwret4{f+{o=0@N_OX-?y|nmJKW)-lPpnk&&bYg%yKrpOdG-}Y5Kyh7Nv ziaXcfz%c;?tYUwJ#=_Gbgb;8_%^3niSKX!$595G{T|^NOv0ESZ>jRkpEL088Jb~<9 z*isw3O~H_@1XLNbMO3D*wJQ#N7P!pfmRcI)t6_(_wxyg1Ud}oW3+47P6sO|}3{+oB z&$`qKiFk+V!-nX{s|~6m;Cc702GG|SO6YwGu_Fn_xwu<|9iX@ql-{NVC$^`9PM0b1 zA3weJld;zxEdh@VX1Jz4W}P{_<(0Gfil~}6um-;Mdne%h$?14!U|{8!Sr#4d%nGdh zF3)aw=Oy%gft9>_j*GyFyx?~mo!IZx3vSM1=U%+B;`t{RjM;ZG*NvPT{(UxHq7AJ4 zJ%r-o`de?q#R)3;zYp$yBOm+XspLDkUEW(S#rAh>>*s9QI~cTgVc*VWk0$2kah_i| zDd*K6^IjYlW%-b?xf2nN4=Il1B%SzJMERV@w!Tqv=@%b=19QILflDrKpo3``|2zKj zZg|U{+(-@{$?W$(DtdP=-b+?DXt!3y_Dl+FipYjJ6_41(0xRX zb0)L7?0)A==6zYDV{0iMYVAIf)kW8pt#f)hdh@ZTAjW%z6B1)el#WLfD}7ZlST$+M zRdjboVT`LThs@7j-d%;A2s4?g=VKBr{S$NZlW)U+ndCX~1h2g}K93=&n zNeW6{1Q~q3#Pq}mNOWvdQJ&GZoFL@u5Kf>5Yi6!sAC5{YNG`7y0RgZ73&0Bco zX{Xgssh_oIA+DCpiUt=hX>JbUlSaX%jm`6BYx2~8U7ytB1n)T=+l%}p>(7kJhEiWBt! zE;Z^~G7ZI0s9uCN`@p^!{nfr>dK@%sEE%UpI%cΠ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

IgA1ytTN=Ik3x^A1?fiDK=l1!atXy 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
HAuXrkj=~-Nr0(#cB!khj&zuzXzsuo|d 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
sBZmb#pJ2N!egvyZJF18^g}!h&GDo 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 fDeYj#Umy?Ae97xSy!-Rx-w9(WodD;m= requested version. +# The variable CVF_VERSION must be set before calling configure_file(). + +set(PACKAGE_VERSION "1.3.3") + +if (PACKAGE_FIND_VERSION_RANGE) + # Package version must be in the requested version range + if ((PACKAGE_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MIN) + OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_GREATER PACKAGE_FIND_VERSION_MAX) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_GREATER_EQUAL PACKAGE_FIND_VERSION_MAX))) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + endif() +else() + if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() + endif() +endif() + + +# if the installed project requested no architecture check, don't perform the check +if("FALSE") + return() +endif() + +# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it: +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "") + return() +endif() + +# check that the installed version has the same 32/64bit-ness as the one which is currently searching: +if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") + math(EXPR installedBits "8 * 8") + set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)") + set(PACKAGE_VERSION_UNSUITABLE TRUE) +endif() diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-config.cmake b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-config.cmake new file mode 100644 index 000000000..580d98613 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-config.cmake @@ -0,0 +1,36 @@ + +####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() ####### +####### Any changes to this file will be overwritten by the next CMake run #### +####### The input file was redis++-config.cmake.in ######## + +get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) + +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !") + endif() +endmacro() + +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() + +#################################################################################### + +include(CMakeFindDependencyMacro) + +string(REPLACE "," ";" REDIS_PLUS_PLUS_DEPENDS_LIST hiredis) +foreach(REDIS_PLUS_PLUS_DEP ${REDIS_PLUS_PLUS_DEPENDS_LIST}) + find_dependency(${REDIS_PLUS_PLUS_DEP} REQUIRED) +endforeach() + +include("${CMAKE_CURRENT_LIST_DIR}/redis++-targets.cmake") + +check_required_components(redis++) diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets-release.cmake b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets-release.cmake new file mode 100644 index 000000000..b790b539f --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets-release.cmake @@ -0,0 +1,19 @@ +#---------------------------------------------------------------- +# Generated CMake target import file for configuration "Release". +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Import target "redis++::redis++_static" for configuration "Release" +set_property(TARGET redis++::redis++_static APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(redis++::redis++_static PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX" + IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libredis++.a" + ) + +list(APPEND _IMPORT_CHECK_TARGETS redis++::redis++_static ) +list(APPEND _IMPORT_CHECK_FILES_FOR_redis++::redis++_static "${_IMPORT_PREFIX}/lib/libredis++.a" ) + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets.cmake b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets.cmake new file mode 100644 index 000000000..9e87d927e --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets.cmake @@ -0,0 +1,94 @@ +# Generated by CMake + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.6) + message(FATAL_ERROR "CMake >= 2.6.0 required") +endif() +cmake_policy(PUSH) +cmake_policy(VERSION 2.6...3.20) +#---------------------------------------------------------------- +# Generated CMake target import file. +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Protect against multiple inclusion, which would fail when already imported targets are added once more. +set(_targetsDefined) +set(_targetsNotDefined) +set(_expectedTargets) +foreach(_expectedTarget redis++::redis++_static) + list(APPEND _expectedTargets ${_expectedTarget}) + if(NOT TARGET ${_expectedTarget}) + list(APPEND _targetsNotDefined ${_expectedTarget}) + endif() + if(TARGET ${_expectedTarget}) + list(APPEND _targetsDefined ${_expectedTarget}) + endif() +endforeach() +if("${_targetsDefined}" STREQUAL "${_expectedTargets}") + unset(_targetsDefined) + unset(_targetsNotDefined) + unset(_expectedTargets) + set(CMAKE_IMPORT_FILE_VERSION) + cmake_policy(POP) + return() +endif() +if(NOT "${_targetsDefined}" STREQUAL "") + message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_targetsDefined}\nTargets not yet defined: ${_targetsNotDefined}\n") +endif() +unset(_targetsDefined) +unset(_targetsNotDefined) +unset(_expectedTargets) + + +# Compute the installation prefix relative to this file. +get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +if(_IMPORT_PREFIX STREQUAL "/") + set(_IMPORT_PREFIX "") +endif() + +# Create imported target redis++::redis++_static +add_library(redis++::redis++_static STATIC IMPORTED) + +set_target_properties(redis++::redis++_static PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include;${_IMPORT_PREFIX}/include" +) + +# Load information for each installed configuration. +get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +file(GLOB CONFIG_FILES "${_DIR}/redis++-targets-*.cmake") +foreach(f ${CONFIG_FILES}) + include(${f}) +endforeach() + +# Cleanup temporary variables. +set(_IMPORT_PREFIX) + +# Loop over all imported files and verify that they actually exist +foreach(target ${_IMPORT_CHECK_TARGETS} ) + foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} ) + if(NOT EXISTS "${file}" ) + message(FATAL_ERROR "The imported target \"${target}\" references the file + \"${file}\" +but this file does not exist. Possible reasons include: +* The file was deleted, renamed, or moved to another location. +* An install or uninstall procedure did not complete successfully. +* The installation package was faulty and contained + \"${CMAKE_CURRENT_LIST_FILE}\" +but not all the files it references. +") + endif() + endforeach() + unset(_IMPORT_CHECK_FILES_FOR_${target}) +endforeach() +unset(_IMPORT_CHECK_TARGETS) + +# This file does not depend on other imported targets which have +# been exported from the same project but in a separate export set. + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) +cmake_policy(POP) diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection.cpp new file mode 100644 index 000000000..6070d7c80 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection.cpp @@ -0,0 +1,448 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "async_connection.h" +#include +#include "errors.h" +#include "async_shards_pool.h" +#include "cmd_formatter.h" + +namespace { + +using namespace sw::redis; + +void set_options_callback(redisAsyncContext *ctx, void *r, void *) { + assert(ctx != nullptr); + + auto *context = static_cast(ctx->data); + assert(context != nullptr); + + auto &connection = context->connection; + assert(connection); + + redisReply *reply = static_cast(r); + if (reply == nullptr) { + // Connection has bee closed. + // TODO: not sure if we should set this to be State::BROKEN + return; + } + + try { + if (reply::is_error(*reply)) { + throw_error(*reply); + } + + reply::parse(*reply); + } catch (const Error &e) { + // TODO: disconnect and connect_callback might throw + connection->disconnect(std::make_exception_ptr(e)); + + return; + } + + connection->connect_callback(); +} + +} + +namespace sw { + +namespace redis { + +AsyncConnection::AsyncConnection(const ConnectionOptions &opts, + EventLoop *loop, + AsyncConnectionMode mode) : + _opts(opts), + _loop(loop), + _create_time(std::chrono::steady_clock::now()), + _last_active(std::chrono::steady_clock::now().time_since_epoch()) { + assert(_loop != nullptr); + + switch (mode) { + case AsyncConnectionMode::SINGLE: + _state = State::NOT_CONNECTED; + break; + + case AsyncConnectionMode::SENTINEL: + _state = State::WAIT_SENTINEL; + break; + + default: + throw Error("not supporeted async connection mode"); + break; + } +} + +AsyncConnection::~AsyncConnection() { + _clean_up(); +} + +void AsyncConnection::send(AsyncEventUPtr event) { + { + std::lock_guard lock(_mtx); + + _events.push_back(std::move(event)); + } + + _loop->add(shared_from_this()); +} + +void AsyncConnection::event_callback() { + // NOTE: we should try our best not throw in these callbacks + switch (_state.load()) { + case State::WAIT_SENTINEL: + _connect_with_sentinel(); + break; + + case State::NOT_CONNECTED: + _connect(); + break; + + case State::READY: + _send(); + break; + + case State::BROKEN: + _clean_up(); + break; + + default: + break; + } +} + +void AsyncConnection::connect_callback(std::exception_ptr err) { + if (err) { + // Failed to connect to Redis, fail all pending events. + _fail_events(err); + + return; + } + + // Connect OK. + try { + switch (_state.load()) { + case State::CONNECTING: + _connecting_callback(); + break; + + case State::AUTHING: + _authing_callback(); + break; + + case State::SELECTING_DB: + _select_db_callback(); + break; + + default: + assert(_state == State::ENABLE_READONLY); + + _set_ready(); + } + } catch (const Error &e) { + disconnect(std::make_exception_ptr(e)); + } +} + +void AsyncConnection::disconnect(std::exception_ptr err) { + if (_ctx != nullptr) { + _disable_disconnect_callback(); + + redisAsyncDisconnect(_ctx); + } + + _fail_events(err); +} + +void AsyncConnection::disconnect_callback(std::exception_ptr err) { + _fail_events(err); +} + +ConnectionOptions AsyncConnection::options() { + std::lock_guard lock(_mtx); + + return _opts; +} + +void AsyncConnection::update_node_info(const std::string &host, int port) { + std::lock_guard lock(_mtx); + + _opts.host = host; + _opts.port = port; +} + +void AsyncConnection::_disable_disconnect_callback() { + assert(_ctx != nullptr); + + auto *ctx = static_cast(_ctx->data); + + assert(ctx != nullptr); + + ctx->run_disconnect_callback = false; +} + +void AsyncConnection::_send() { + auto events = _get_events(); + auto &ctx = _context(); + for (auto idx = 0U; idx != events.size(); ++idx) { + auto &event = events[idx]; + try { + event->handle(ctx); + + // CommandEvent::_reply_callback will release the memory. + event.release(); + } catch (...) { + // Failed to send command, fail subsequent events. + auto err = std::current_exception(); + for (; idx != events.size(); ++idx) { + auto &event = events[idx]; + event->set_exception(err); + } + + disconnect(err); + + break; + } + } +} + +std::vector AsyncConnection::_get_events() { + std::vector events; + { + std::lock_guard lock(_mtx); + + events.swap(_events); + } + + return events; +} + +void AsyncConnection::_clean_up() { + if (!_err) { + _err = std::make_exception_ptr(Error("connection is closing")); + } + + auto events = _get_events(); + for (auto &event : events) { + assert(event); + + event->set_exception(_err); + } +} + +void AsyncConnection::_fail_events(std::exception_ptr err) { + _ctx = nullptr; + + _err = err; + + _state = State::BROKEN; + + // Must call _clean_up after `_err` has been set. + _clean_up(); +} + +void AsyncConnection::_connecting_callback() { + if (_need_auth()) { + _auth(); + } else if (_need_select_db()) { + _select_db(); + } else if (_need_enable_readonly()) { + _enable_readonly(); + } else { + _set_ready(); + } +} + +void AsyncConnection::_authing_callback() { + if (_need_select_db()) { + _select_db(); + } else if (_need_enable_readonly()) { + _enable_readonly(); + } else { + _set_ready(); + } +} + +void AsyncConnection::_select_db_callback() { + if (_need_enable_readonly()) { + _enable_readonly(); + } else { + _set_ready(); + } +} + +void AsyncConnection::_auth() { + assert(!broken()); + + if (_opts.user == "default") { + if (redisAsyncCommand(_ctx, set_options_callback, nullptr, "AUTH %b", + _opts.password.data(), _opts.password.size()) != REDIS_OK) { + throw Error("failed to send auth command"); + } + } else { + // Redis 6.0 or latter + if (redisAsyncCommand(_ctx, set_options_callback, nullptr, "AUTH %b %b", + _opts.user.data(), _opts.user.size(), + _opts.password.data(), _opts.password.size()) != REDIS_OK) { + throw Error("failed to send auth command"); + } + } + + _state = State::AUTHING; +} + +void AsyncConnection::_select_db() { + assert(!broken()); + + if (redisAsyncCommand(_ctx, set_options_callback, nullptr, "SELECT %d", + _opts.db) != REDIS_OK) { + throw Error("failed to send select command"); + } + + _state = State::SELECTING_DB; +} + +void AsyncConnection::_enable_readonly() { + assert(!broken()); + + if (redisAsyncCommand(_ctx, set_options_callback, nullptr, "READONLY") != REDIS_OK) { + throw Error("failed to send readonly command"); + } + + _state = State::ENABLE_READONLY; +} + +void AsyncConnection::_set_ready() { + _state = State::READY; + + // Send pending commands. + _send(); +} + +void AsyncConnection::_connect_with_sentinel() { + try { + auto opts = options(); + if (opts.host.empty()) { + // Still waiting for sentinel. + return; + } + + // Already got node info from sentinel + _state = State::NOT_CONNECTED; + + _connect(); + } catch (const Error &err) { + _fail_events(std::current_exception()); + } +} + +void AsyncConnection::_connect() { + try { + auto opts = options(); + + auto ctx = _connect(opts); + + assert(ctx && ctx->err == REDIS_OK); + + const auto &tls_opts = opts.tls; + tls::TlsContextUPtr tls_ctx; + if (tls::enabled(tls_opts)) { + tls_ctx = tls::secure_connection(ctx->c, tls_opts); + } + + _loop->watch(*ctx); + + _tls_ctx = std::move(tls_ctx); + _ctx = ctx.release(); + + _state = State::CONNECTING; + } catch (const Error &err) { + _fail_events(std::current_exception()); + } +} + +bool AsyncConnection::_need_auth() const { + return !_opts.password.empty() || _opts.user != "default"; +} + +bool AsyncConnection::_need_select_db() const { + return _opts.db != 0; +} + +bool AsyncConnection::_need_enable_readonly() const { + return _opts.readonly; +} + +void AsyncConnection::_clean_async_context(void *data) { + auto *ctx = static_cast(data); + + assert(ctx != nullptr); + + delete ctx; +} + +AsyncConnection::AsyncContextUPtr AsyncConnection::_connect(const ConnectionOptions &opts) { + redisAsyncContext *context = nullptr; + switch (opts.type) { + case ConnectionType::TCP: + context = redisAsyncConnect(opts.host.c_str(), opts.port); + break; + + case ConnectionType::UNIX: + context = redisAsyncConnectUnix(opts.path.c_str()); + break; + + default: + // Never goes here. + throw Error("Unknown connection type"); + } + + if (context == nullptr) { + throw Error("Failed to allocate memory for connection."); + } + + auto ctx = AsyncContextUPtr(context); + if (ctx->err != REDIS_OK) { + throw_error(ctx->c, "failed to connect to server"); + } + + ctx->data = new AsyncContext(shared_from_this()); + ctx->dataCleanup = _clean_async_context; + + return ctx; +} + +GuardedAsyncConnection::GuardedAsyncConnection(const AsyncConnectionPoolSPtr &pool) : + _pool(pool), _connection(_pool->fetch()) { + assert(!_connection->broken()); +} + +GuardedAsyncConnection::~GuardedAsyncConnection() { + // If `GuardedAsyncConnection` has been moved, `_pool` will be nullptr. + if (_pool) { + _pool->release(std::move(_connection)); + } +} + +AsyncConnection& GuardedAsyncConnection::connection() { + assert(_connection); + + return *_connection; +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection.h new file mode 100644 index 000000000..727a16be2 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection.h @@ -0,0 +1,498 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ASYNC_CONNECTION_H +#define SEWENEW_REDISPLUSPLUS_ASYNC_CONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include "connection.h" +#include "command_args.h" +#include "event_loop.h" +#include "async_utils.h" +#include "tls.h" +#include "shards.h" +#include "cmd_formatter.h" + +namespace sw { + +namespace redis { + +template +struct DefaultResultParser { + Result operator()(redisReply &reply) const { + return reply::parse(reply); + } +}; + +class AsyncConnection; +class AsyncConnectionPool; +class AsyncShardsPool; + +class AsyncEvent { +public: + virtual ~AsyncEvent() = default; + + virtual void handle(redisAsyncContext &ctx) = 0; + + virtual void set_exception(std::exception_ptr err) = 0; +}; + +using AsyncEventUPtr = std::unique_ptr; + +enum class AsyncConnectionMode { + SINGLE = 0, + SENTINEL, + CLUSTER +}; + +class AsyncConnection : public std::enable_shared_from_this { +public: + AsyncConnection(const ConnectionOptions &opts, + EventLoop *loop, + AsyncConnectionMode = AsyncConnectionMode::SINGLE); + + AsyncConnection(const AsyncConnection &) = delete; + AsyncConnection& operator=(const AsyncConnection &) = delete; + + AsyncConnection(AsyncConnection &&) = delete; + AsyncConnection& operator=(AsyncConnection &&) = delete; + + ~AsyncConnection(); + + bool broken() const noexcept { + return _state == State::BROKEN; + } + + auto create_time() const + -> std::chrono::time_point { + return _create_time; + } + + auto last_active() const + -> std::chrono::steady_clock::duration { + return _last_active; + } + + void disconnect(std::exception_ptr err); + + template + Future send(FormattedCommand cmd); + + template + Future send(const std::shared_ptr &pool, + const StringView &key, + FormattedCommand cmd); + + void send(AsyncEventUPtr event); + + void event_callback(); + + void connect_callback(std::exception_ptr err = nullptr); + + void disconnect_callback(std::exception_ptr err); + + ConnectionOptions options(); + + void update_node_info(const std::string &host, int port); + +private: + enum class State { + BROKEN = 0, + NOT_CONNECTED, + CONNECTING, + AUTHING, + SELECTING_DB, + READY, + WAIT_SENTINEL, + ENABLE_READONLY + }; + + redisAsyncContext& _context() { + assert(_ctx != nullptr); + + _last_active = std::chrono::steady_clock::now().time_since_epoch(); + + return *_ctx; + } + + void _connecting_callback(); + + void _authing_callback(); + + void _select_db_callback(); + + bool _need_auth() const; + + void _auth(); + + bool _need_select_db() const; + + void _select_db(); + + bool _need_enable_readonly() const; + + void _enable_readonly(); + + void _set_ready(); + + void _connect_with_sentinel(); + + void _connect(); + + void _disable_disconnect_callback(); + + void _send(); + + std::vector> _get_events(); + + void _clean_up(); + + void _fail_events(std::exception_ptr err); + + static void _clean_async_context(void *data); + + struct AsyncContextDeleter { + void operator()(redisAsyncContext *ctx) const { + if (ctx != nullptr) { + redisAsyncFree(ctx); + } + } + }; + using AsyncContextUPtr = std::unique_ptr; + + AsyncContextUPtr _connect(const ConnectionOptions &opts); + + ConnectionOptions _opts; + + EventLoop *_loop = nullptr; + + tls::TlsContextUPtr _tls_ctx; + + // _ctx will be release by EventLoop after attached. + redisAsyncContext *_ctx = nullptr; + + // The time that the connection is created. + std::chrono::time_point _create_time{}; + + // The time that the connection is created or the time that + // the connection is recently used, i.e. `_context()` is called. + // NOTE: `_last_active` is `std::atomic`, and we cannot make it of type time_point, + // since time_point's constructor is non-trival. + std::atomic _last_active{}; + + std::vector> _events; + + std::atomic _state{State::NOT_CONNECTED}; + + std::exception_ptr _err; + + std::mutex _mtx; +}; + +using AsyncConnectionSPtr = std::shared_ptr; + +struct AsyncContext { + AsyncContext(AsyncConnectionSPtr conn) : connection(std::move(conn)) {} + + AsyncConnectionSPtr connection; + + bool run_disconnect_callback = true; +}; + +template +class CommandEvent : public AsyncEvent { +public: + explicit CommandEvent(FormattedCommand cmd) : _cmd(std::move(cmd)) {} + + Future get_future() { + return _pro.get_future(); + } + + virtual void handle(redisAsyncContext &ctx) override { + _handle(ctx, _reply_callback); + } + + virtual void set_exception(std::exception_ptr err) override { + _pro.set_exception(err); + } + + template + struct ResultType {}; + + void set_value(redisReply &reply) { + _set_value(reply, ResultType{}); + } + +protected: + using Callback = void (*)(redisAsyncContext *, void *, void *); + + void _handle(redisAsyncContext &ctx, Callback callback) { + if (redisAsyncFormattedCommand(&ctx, + callback, this, _cmd.data(), _cmd.size()) != REDIS_OK) { + throw_error(ctx.c, "failed to send command"); + } + } + +private: + static void _reply_callback(redisAsyncContext * /*ctx*/, void *r, void *privdata) { + auto event = static_cast *>(privdata); + + assert(event != nullptr); + + try { + redisReply *reply = static_cast(r); + if (reply == nullptr) { + event->set_exception(std::make_exception_ptr(Error("connection has been closed"))); + } else if (reply::is_error(*reply)) { + try { + throw_error(*reply); + } catch (const Error &e) { + event->set_exception(std::current_exception()); + } + } else { + event->set_value(*reply); + } + } catch (...) { + event->set_exception(std::current_exception()); + } + + delete event; + } + + template + void _set_value(redisReply &reply, ResultType) { + ResultParser parser; + _pro.set_value(parser(reply)); + } + + void _set_value(redisReply &reply, ResultType) { + ResultParser parser; + parser(reply); + + _pro.set_value(); + } + + FormattedCommand _cmd; + + Promise _pro; +}; + +template +using CommandEventUPtr = std::unique_ptr>; + +class AskingEvent : public AsyncEvent { +public: + explicit AskingEvent(AsyncEvent *event) : _event(event) {} + + ~AskingEvent() { + if (_event != nullptr) { + delete _event; + } + } + + virtual void handle(redisAsyncContext &ctx) override { + if (redisAsyncCommand(&ctx, _asking_callback, this, "ASKING") != REDIS_OK) { + throw_error(ctx.c, "failed to send ASKING command"); + } + + assert(_event != nullptr); + + _event->handle(ctx); + + _event = nullptr; + } + + virtual void set_exception(std::exception_ptr err) override { + if (_event != nullptr) { + _event->set_exception(err); + } + } + +private: + static void _asking_callback(redisAsyncContext * /*ctx*/, void *r, void *privdata) { + auto event = static_cast(privdata); + + assert(event != nullptr); + + // TODO: No need to check the reply. It seems that we can simply ignore the reply, + // and delete the event. + try { + redisReply *reply = static_cast(r); + if (reply == nullptr) { + event->set_exception(std::make_exception_ptr(Error("connection has been closed"))); + } else if (reply::is_error(*reply)) { + try { + throw_error(*reply); + } catch (const Error &e) { + event->set_exception(std::current_exception()); + } + } else { + reply::parse(*reply); + } + } catch (...) { + event->set_exception(std::current_exception()); + } + + delete event; + } + + AsyncEvent *_event = nullptr; +}; + +// NOTE: This class is similar to `SafeAsyncConnection`. +// The difference is that `SafeAsyncConnection` tries to avoid copying a std::shared_ptr. +class GuardedAsyncConnection { +public: + explicit GuardedAsyncConnection(const std::shared_ptr &pool); + + GuardedAsyncConnection(const GuardedAsyncConnection &) = delete; + GuardedAsyncConnection& operator=(const GuardedAsyncConnection &) = delete; + + GuardedAsyncConnection(GuardedAsyncConnection &&) = default; + GuardedAsyncConnection& operator=(GuardedAsyncConnection &&) = default; + + ~GuardedAsyncConnection(); + + AsyncConnection& connection(); + +private: + std::shared_ptr _pool; + std::shared_ptr _connection; +}; + +template +class ClusterEvent : public CommandEvent { +public: + explicit ClusterEvent(const std::shared_ptr &pool, + const StringView &key, + FormattedCommand cmd) : + CommandEvent(std::move(cmd)), + _pool(pool), + _key(key.data(), key.size()) {} + + virtual void handle(redisAsyncContext &ctx) override { + CommandEvent::_handle(ctx, _cluster_reply_callback); + } + +private: + enum class State { + NORMAL = 0, + MOVED, + ASKING + }; + + static void _cluster_reply_callback(redisAsyncContext * /*ctx*/, void *r, void *privdata) { + auto event = static_cast *>(privdata); + + assert(event != nullptr); + + try { + redisReply *reply = static_cast(r); + if (reply == nullptr) { + event->set_exception(std::make_exception_ptr(Error("connection has been closed"))); + } else if (reply::is_error(*reply)) { + try { + throw_error(*reply); + } catch (const IoError &err) { + event->_pool->update(event->_key, AsyncEventUPtr(event)); + return; + } catch (const ClosedError &err) { + event->_pool->update(event->_key, AsyncEventUPtr(event)); + return; + } catch (const MovedError &err) { + switch (event->_state) { + case State::MOVED: + throw Error("too many moved error"); + break; + + case State::ASKING: + throw Error("Slot migrating..."); + break; + + default: + break; + } + + event->_state = State::MOVED; + event->_pool->update(event->_key, AsyncEventUPtr(event)); + return; + } catch (const AskError &err) { + event->_state = State::ASKING; + auto pool = event->_pool->fetch(err.node()); + assert(pool); + GuardedAsyncConnection connection(pool); + connection.connection().send(AsyncEventUPtr(new AskingEvent(event))); + return; + } catch (const Error &e) { + event->set_exception(std::current_exception()); + } + } else { + event->set_value(*reply); + } + } catch (...) { + event->set_exception(std::current_exception()); + } + + delete event; + } + + std::shared_ptr _pool; + + std::string _key; + + State _state = State::NORMAL; +}; + +template +using ClusterEventUPtr = std::unique_ptr>; + +template +Future AsyncConnection::send(FormattedCommand cmd) { + auto event = CommandEventUPtr( + new CommandEvent(std::move(cmd))); + + auto fut = event->get_future(); + + send(std::move(event)); + + return fut; +} + +template +Future AsyncConnection::send(const std::shared_ptr &pool, + const StringView &key, + FormattedCommand cmd) { + auto event = ClusterEventUPtr( + new ClusterEvent(pool, key, std::move(cmd))); + + auto fut = event->get_future(); + + send(std::move(event)); + + return fut; +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ASYNC_CONNECTION_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection_pool.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection_pool.cpp new file mode 100644 index 000000000..f00d57bb8 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection_pool.cpp @@ -0,0 +1,347 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "async_connection_pool.h" +#include +#include +#include "errors.h" + +namespace sw { + +namespace redis { + +SimpleAsyncSentinel::SimpleAsyncSentinel(const AsyncSentinelSPtr &sentinel, + const std::string &master_name, + Role role) : + _sentinel(sentinel), + _master_name(master_name), + _role(role) { + if (!_sentinel) { + throw Error("Sentinel cannot be null"); + } + + if (_role != Role::MASTER && _role != Role::SLAVE) { + throw Error("Role must be Role::MASTER or Role::SLAVE"); + } +} + +AsyncConnectionSPtr SimpleAsyncSentinel::create(const ConnectionOptions &opts, + const std::shared_ptr &pool, + EventLoop *loop) { + auto connection = std::make_shared(opts, loop, AsyncConnectionMode::SENTINEL); + + AsyncSentinel::AsyncSentinelTask task; + task.pool = pool; + task.connection = connection; + task.master_name = _master_name; + task.role = _role; + + _sentinel->add(std::move(task)); + + return connection; +} + +AsyncConnectionPool::AsyncConnectionPool(const EventLoopSPtr &loop, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts) : + _loop(loop), + _opts(connection_opts), + _pool_opts(pool_opts) { + if (_pool_opts.size == 0) { + throw Error("CANNOT create an empty pool"); + } + + // Lazily create connections. +} + +AsyncConnectionPool::AsyncConnectionPool(SimpleAsyncSentinel sentinel, + const EventLoopSPtr &loop, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts) : + _loop(loop), + _opts(connection_opts), + _pool_opts(pool_opts), + _sentinel(std::move(sentinel)) { + // In this case, the connection must be of TCP type. + if (_opts.type != ConnectionType::TCP) { + throw Error("Sentinel only supports TCP connection"); + } + + if (_opts.connect_timeout == std::chrono::milliseconds(0) + || _opts.socket_timeout == std::chrono::milliseconds(0)) { + throw Error("With sentinel, connection timeout and socket timeout cannot be 0"); + } + + // Cleanup connection options. + _update_connection_opts("", -1); + + assert(_sentinel); +} + +AsyncConnectionPool::AsyncConnectionPool(AsyncConnectionPool &&that) { + std::lock_guard lock(that._mutex); + + _move(std::move(that)); +} + +AsyncConnectionPool& AsyncConnectionPool::operator=(AsyncConnectionPool &&that) { + if (this != &that) { + std::lock(_mutex, that._mutex); + std::lock_guard lock_this(_mutex, std::adopt_lock); + std::lock_guard lock_that(that._mutex, std::adopt_lock); + + _move(std::move(that)); + } + + return *this; +} + +AsyncConnectionPool::~AsyncConnectionPool() { + assert(_loop); + + // TODO: what if the connection has been borrowed but not returned? + // Or we dont' need to worry about that, since it's destructing and + // all borrowed connections should have been returned. + for (auto &connection : _pool) { + // TODO: what if some connection has never been watched? Is it possible? + _loop->unwatch(std::move(connection)); + } +} + +AsyncConnectionSPtr AsyncConnectionPool::fetch() { + std::unique_lock lock(_mutex); + + if (_pool.empty()) { + if (_used_connections == _pool_opts.size) { + _wait_for_connection(lock); + } else { + // Lazily create a new connection. + auto connection = _create(); + + ++_used_connections; + + return connection; + } + } + + // _pool is NOT empty. + auto connection = _fetch(); + + auto connection_lifetime = _pool_opts.connection_lifetime; + auto connection_idle_time = _pool_opts.connection_idle_time; + + if (_sentinel) { + auto opts = _opts; + auto role_changed = _role_changed(connection->options()); + auto sentinel = _sentinel; + + lock.unlock(); + + if (role_changed || _need_reconnect(*connection, connection_lifetime, connection_idle_time)) { + try { + auto tmp_connection = sentinel.create(opts, shared_from_this(), _loop.get()); + + std::swap(tmp_connection, connection); + + // Release expired connection. + // TODO: If `unwatch` throw, we will leak the connection. + _loop->unwatch(std::move(tmp_connection)); + } catch (const Error &e) { + // Failed to reconnect, return it to the pool, and retry latter. + release(std::move(connection)); + throw; + } + } + + return connection; + } + + lock.unlock(); + + assert(connection); + + if (_need_reconnect(*connection, connection_lifetime, connection_idle_time)) { + try { + auto tmp_connection = _create(); + + std::swap(tmp_connection, connection); + + // Release expired connection. + // TODO: If `unwatch` throw, we will leak the connection. + _loop->unwatch(std::move(tmp_connection)); + } catch (const Error &e) { + // Failed, return it to the pool, and retry latter. + release(std::move(connection)); + throw; + } + } + + return connection; +} + +ConnectionOptions AsyncConnectionPool::connection_options() { + std::lock_guard lock(_mutex); + + return _opts; +} + +void AsyncConnectionPool::release(AsyncConnectionSPtr connection) { + { + std::lock_guard lock(_mutex); + + _pool.push_back(std::move(connection)); + } + + _cv.notify_one(); +} + +AsyncConnectionSPtr AsyncConnectionPool::create() { + std::unique_lock lock(_mutex); + + auto opts = _opts; + + if (_sentinel) { + // TODO: it seems that we don't need to copy sentinel, + // since it's thread-safe. + auto sentinel = _sentinel; + + lock.unlock(); + + return sentinel.create(opts, shared_from_this(), _loop.get()); + } else { + lock.unlock(); + + return std::make_shared(opts, _loop.get()); + } +} + +AsyncConnectionPool AsyncConnectionPool::clone() { + std::unique_lock lock(_mutex); + + auto opts = _opts; + auto pool_opts = _pool_opts; + + if (_sentinel) { + auto sentinel = _sentinel; + + lock.unlock(); + + return AsyncConnectionPool(sentinel, _loop, pool_opts, opts); + } else { + lock.unlock(); + + return AsyncConnectionPool(_loop, pool_opts, opts); + } +} + +void AsyncConnectionPool::update_node_info(const std::string &host, + int port, + AsyncConnectionSPtr &connection) { + { + std::lock_guard lock(_mutex); + + _update_connection_opts(host, port); + } + + connection->update_node_info(host, port); + + _loop->add(connection); +} + +void AsyncConnectionPool::update_node_info(AsyncConnectionSPtr &connection, + std::exception_ptr err) { + _loop->unwatch(connection, err); +} + +void AsyncConnectionPool::_move(AsyncConnectionPool &&that) { + _loop = std::move(that._loop); + _opts = std::move(that._opts); + _pool_opts = std::move(that._pool_opts); + _pool = std::move(that._pool); + _used_connections = that._used_connections; + _sentinel = std::move(that._sentinel); +} + +AsyncConnectionSPtr AsyncConnectionPool::_create() { + if (_sentinel) { + // Get Redis host and port info from sentinel. + // In this case, the mutex has been locked. + return _sentinel.create(_opts, shared_from_this(), _loop.get()); + } + + return std::make_shared(_opts, _loop.get()); +} + +AsyncConnectionSPtr AsyncConnectionPool::_fetch() { + assert(!_pool.empty()); + + auto connection = std::move(_pool.front()); + _pool.pop_front(); + + return connection; +} + +void AsyncConnectionPool::_wait_for_connection(std::unique_lock &lock) { + auto timeout = _pool_opts.wait_timeout; + if (timeout > std::chrono::milliseconds(0)) { + // Wait until _pool is no longer empty or timeout. + if (!_cv.wait_for(lock, + timeout, + [this] { return !(this->_pool).empty(); })) { + throw Error("Failed to fetch a connection in " + + std::to_string(timeout.count()) + " milliseconds"); + } + } else { + // Wait forever. + _cv.wait(lock, [this] { return !(this->_pool).empty(); }); + } +} + +bool AsyncConnectionPool::_need_reconnect(const AsyncConnection &connection, + const std::chrono::milliseconds &connection_lifetime, + const std::chrono::milliseconds &connection_idle_time) const { + if (connection.broken()) { + return true; + } + + auto now = std::chrono::steady_clock::now(); + if (connection_lifetime > std::chrono::milliseconds(0)) { + if (now - connection.create_time() > connection_lifetime) { + return true; + } + } + + if (connection_idle_time > std::chrono::milliseconds(0)) { + if (now.time_since_epoch() - connection.last_active() > connection_idle_time) { + return true; + } + } + + return false; +} + +bool AsyncConnectionPool::_role_changed(const ConnectionOptions &opts) const { + if (opts.host.empty()) { + // Still waiting for sentinel. + return false; + } + + return opts.port != _opts.port || opts.host != _opts.host; +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection_pool.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection_pool.h new file mode 100644 index 000000000..17aef68a9 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_connection_pool.h @@ -0,0 +1,181 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ASYNC_CONNECTION_POOL_H +#define SEWENEW_REDISPLUSPLUS_ASYNC_CONNECTION_POOL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "connection.h" +#include "connection_pool.h" +#include "async_connection.h" +#include "async_sentinel.h" + +namespace sw { + +namespace redis { + +class AsyncConnectionPool; + +class SimpleAsyncSentinel { +public: + SimpleAsyncSentinel(const AsyncSentinelSPtr &sentinel, + const std::string &master_name, + Role role); + + SimpleAsyncSentinel() = default; + + SimpleAsyncSentinel(const SimpleAsyncSentinel &) = default; + SimpleAsyncSentinel& operator=(const SimpleAsyncSentinel &) = default; + + SimpleAsyncSentinel(SimpleAsyncSentinel &&) = default; + SimpleAsyncSentinel& operator=(SimpleAsyncSentinel &&) = default; + + ~SimpleAsyncSentinel() = default; + + explicit operator bool() const { + return bool(_sentinel); + } + + AsyncConnectionSPtr create(const ConnectionOptions &opts, + const std::shared_ptr &pool, + EventLoop *loop); + +private: + AsyncSentinelSPtr _sentinel; + + std::string _master_name; + + Role _role = Role::MASTER; +}; + +class AsyncConnectionPool : public std::enable_shared_from_this { +public: + AsyncConnectionPool(const EventLoopSPtr &loop, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + AsyncConnectionPool(SimpleAsyncSentinel sentinel, + const EventLoopSPtr &loop, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + AsyncConnectionPool(AsyncConnectionPool &&that); + AsyncConnectionPool& operator=(AsyncConnectionPool &&that); + + AsyncConnectionPool(const AsyncConnectionPool &) = delete; + AsyncConnectionPool& operator=(const AsyncConnectionPool &) = delete; + + ~AsyncConnectionPool(); + + // Fetch a connection from pool. + AsyncConnectionSPtr fetch(); + + ConnectionOptions connection_options(); + + void release(AsyncConnectionSPtr connection); + + // Create a new connection. + AsyncConnectionSPtr create(); + + AsyncConnectionPool clone(); + + // These update_node_info overloads are called by AsyncSentinel. + void update_node_info(const std::string &host, + int port, + AsyncConnectionSPtr &connection); + + void update_node_info(AsyncConnectionSPtr &connection, + std::exception_ptr err); + +private: + void _move(AsyncConnectionPool &&that); + + // NOT thread-safe + AsyncConnectionSPtr _create(); + + AsyncConnectionSPtr _fetch(); + + void _wait_for_connection(std::unique_lock &lock); + + bool _need_reconnect(const AsyncConnection &connection, + const std::chrono::milliseconds &connection_lifetime, + const std::chrono::milliseconds &connection_idle_time) const; + + void _update_connection_opts(const std::string &host, int port) { + _opts.host = host; + _opts.port = port; + } + + bool _role_changed(const ConnectionOptions &opts) const; + + EventLoopSPtr _loop; + + ConnectionOptions _opts; + + ConnectionPoolOptions _pool_opts; + + std::deque _pool; + + std::size_t _used_connections = 0; + + std::mutex _mutex; + + std::condition_variable _cv; + + SimpleAsyncSentinel _sentinel; +}; + +using AsyncConnectionPoolSPtr = std::shared_ptr; + +class SafeAsyncConnection { +public: + explicit SafeAsyncConnection(AsyncConnectionPool &pool) : _pool(pool), _connection(_pool.fetch()) { + assert(_connection); + } + + SafeAsyncConnection(const SafeAsyncConnection &) = delete; + SafeAsyncConnection& operator=(const SafeAsyncConnection &) = delete; + + SafeAsyncConnection(SafeAsyncConnection &&) = delete; + SafeAsyncConnection& operator=(SafeAsyncConnection &&) = delete; + + ~SafeAsyncConnection() { + _pool.release(std::move(_connection)); + } + + AsyncConnection& connection() { + assert(_connection); + + return *_connection; + } + +private: + AsyncConnectionPool &_pool; + AsyncConnectionSPtr _connection; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ASYNC_CONNECTION_POOL_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis++.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis++.h new file mode 100644 index 000000000..364ba5882 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis++.h @@ -0,0 +1,23 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ASYNC_REDISPLUSPLUS_H +#define SEWENEW_REDISPLUSPLUS_ASYNC_REDISPLUSPLUS_H + +#include "async_redis.h" +#include "async_redis_cluster.h" + +#endif // end SEWENEW_REDISPLUSPLUS_ASYNC_REDISPLUSPLUS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis.cpp new file mode 100644 index 000000000..6d9d35ccc --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis.cpp @@ -0,0 +1,52 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "async_redis.h" +#include "reply.h" + +namespace sw { + +namespace redis { + +AsyncRedis::AsyncRedis(const ConnectionOptions &opts, + const ConnectionPoolOptions &pool_opts, + const EventLoopSPtr &loop) : _loop(loop) { + if (!_loop) { + _loop = std::make_shared(); + } + + _pool = std::make_shared(_loop, pool_opts, opts); +} + +AsyncRedis::AsyncRedis(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role, + const ConnectionOptions &opts, + const ConnectionPoolOptions &pool_opts, + const EventLoopSPtr &loop) : _loop(loop) { + if (!_loop) { + _loop = std::make_shared(); + } + + _pool = std::make_shared(SimpleAsyncSentinel(sentinel, master_name, role), + _loop, + pool_opts, + opts); +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis.h new file mode 100644 index 000000000..726db54ff --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis.h @@ -0,0 +1,789 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ASYNC_REDIS_H +#define SEWENEW_REDISPLUSPLUS_ASYNC_REDIS_H + +#include "async_connection.h" +#include "async_connection_pool.h" +#include "async_sentinel.h" +#include "event_loop.h" +#include "utils.h" +#include "command.h" +#include "command_args.h" +#include "command_options.h" +#include "cmd_formatter.h" + +namespace sw { + +namespace redis { + +class AsyncRedis { +public: + AsyncRedis(const ConnectionOptions &opts, + const ConnectionPoolOptions &pool_opts = {}, + const EventLoopSPtr &loop = nullptr); + + AsyncRedis(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role, + const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}, + const EventLoopSPtr &loop = nullptr); + + AsyncRedis(const AsyncRedis &) = delete; + AsyncRedis& operator=(const AsyncRedis &) = delete; + + AsyncRedis(AsyncRedis &&) = default; + AsyncRedis& operator=(AsyncRedis &&) = default; + + ~AsyncRedis() = default; + + template + Future command(const StringView &cmd_name, Args &&...args) { + auto formatter = [](const StringView &cmd_name, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, std::forward(args)...); + return fmt::format_cmd(cmd_args); + }; + + return _command(formatter, cmd_name, std::forward(args)...); + } + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Future>::type { + auto formatter = [](Input first, Input last) { + CmdArgs cmd_args; + while (first != last) { + cmd_args.append(*first); + ++first; + } + return fmt::format_cmd(cmd_args); + }; + + return _command(formatter, first, last); + } + + // CONNECTION commands. + + Future echo(const StringView &msg) { + return _command(fmt::echo, msg); + } + + Future ping() { + return _command(fmt::ping); + } + + Future ping(const StringView &msg) { + return _command(fmt::ping, msg); + } + + // KEY commands. + + Future del(const StringView &key) { + return _command(fmt::del, key); + } + + template + Future del(Input first, Input last) { + range_check("DEL", first, last); + + return _command(fmt::del_range, first, last); + } + + template + Future del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + Future exists(const StringView &key) { + return _command(fmt::exists, key); + } + + template + Future exists(Input first, Input last) { + range_check("EXISTS", first, last); + + return _command(fmt::exists_range, first, last); + } + + template + Future exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + Future expire(const StringView &key, const std::chrono::seconds &timeout) { + return _command(fmt::expire, key, timeout); + } + + Future expireat(const StringView &key, + const std::chrono::time_point &tp) { + return _command(fmt::expireat, key, tp); + } + + Future pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return _command(fmt::pexpire, key, timeout); + } + + Future pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return _command(fmt::pexpireat, key, tp); + } + + Future pttl(const StringView &key) { + return _command(fmt::pttl, key); + } + + Future rename(const StringView &key, const StringView &newkey) { + return _command(fmt::rename, key, newkey); + } + + Future renamenx(const StringView &key, const StringView &newkey) { + return _command(fmt::renamenx, key, newkey); + } + + Future ttl(const StringView &key) { + return _command(fmt::ttl, key); + } + + Future unlink(const StringView &key) { + return _command(fmt::unlink, key); + } + + template + Future unlink(Input first, Input last) { + range_check("UNLINK", first, last); + + return _command(fmt::unlink_range, first, last); + } + + template + Future unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + // STRING commands. + + Future get(const StringView &key) { + return _command(fmt::get, key); + } + + Future incr(const StringView &key) { + return _command(fmt::incr, key); + } + + Future incrby(const StringView &key, long long increment) { + return _command(fmt::incrby, key, increment); + } + + Future incrbyfloat(const StringView &key, double increment) { + return _command(fmt::incrbyfloat, key, increment); + } + + template + Future mget(Input first, Input last) { + range_check("MGET", first, last); + + return _command(fmt::mget, first, last); + } + + template + Future mget(std::initializer_list il) { + return mget(il.begin(), il.end()); + } + + template + Future mset(Input first, Input last) { + range_check("MSET", first, last); + + return _command(fmt::mset, first, last); + } + + template + Future mset(std::initializer_list il) { + return mset(il.begin(), il.end()); + } + + template + Future msetnx(Input first, Input last) { + range_check("MSETNX", first, last); + + return _command(fmt::msetnx, first, last); + } + + template + Future msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + Future set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS) { + return _command_with_parser(fmt::set, key, val, ttl, type); + } + + Future strlen(const StringView &key) { + return _command(fmt::strlen, key); + } + + // LIST commands. + + Future blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return _command(fmt::blpop, key, timeout); + } + + template + Future blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + range_check("BLPOP", first, last); + + return _command(fmt::blpop_range, first, last, timeout); + } + + template + Future blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + Future brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return _command(fmt::brpop, key, timeout); + } + + template + Future brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + range_check("BRPOP", first, last); + + return _command(fmt::brpop_range, first, last, timeout); + } + + template + Future brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + Future brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return _command(fmt::brpoplpush, source, destination, timeout); + } + + Future llen(const StringView &key) { + return _command(fmt::llen, key); + } + + Future lpop(const StringView &key) { + return _command(fmt::lpop, key); + } + + Future lpush(const StringView &key, const StringView &val) { + return _command(fmt::lpush, key, val); + } + + template + Future lpush(const StringView &key, Input first, Input last) { + range_check("LPUSH", first, last); + + return _command(fmt::lpush_range, key, first, last); + } + + template + Future lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + template + Future lrange(const StringView &key, long long start, long long stop) { + return _command(fmt::lrange, key, start, stop); + } + + Future lrem(const StringView &key, long long count, const StringView &val) { + return _command(fmt::lrem, key, count, val); + } + + Future ltrim(const StringView &key, long long start, long long stop) { + return _command(fmt::ltrim, key, start, stop); + } + + Future rpop(const StringView &key) { + return _command(fmt::rpop, key); + } + + Future rpoplpush(const StringView &source, const StringView &destination) { + return _command(fmt::rpoplpush, source, destination); + } + + Future rpush(const StringView &key, const StringView &val) { + return _command(fmt::rpush, key, val); + } + + template + Future rpush(const StringView &key, Input first, Input last) { + range_check("RPUSH", first, last); + + return _command(fmt::rpush_range, key, first, last); + } + + template + Future rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + // HASH commands. + + Future hdel(const StringView &key, const StringView &field) { + return _command(fmt::hdel, key, field); + } + + template + Future hdel(const StringView &key, Input first, Input last) { + range_check("HDEL", first, last); + + return _command(fmt::hdel_range, key, first, last); + } + + template + Future hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + Future hexists(const StringView &key, const StringView &field) { + return _command(fmt::hexists, key, field); + } + + Future hget(const StringView &key, const StringView &field) { + return _command(fmt::hget, key, field); + } + + template + Future hgetall(const StringView &key) { + return _command(fmt::hgetall, key); + } + + Future hincrby(const StringView &key, const StringView &field, long long increment) { + return _command(fmt::hincrby, key, field, increment); + } + + Future hincrbyfloat(const StringView &key, const StringView &field, double increment) { + return _command(fmt::hincrbyfloat, key, field, increment); + } + + template + Future hkeys(const StringView &key) { + return _command(fmt::hkeys, key); + } + + Future hlen(const StringView &key) { + return _command(fmt::hlen, key); + } + + template + Future hmget(const StringView &key, Input first, Input last) { + range_check("HMGET", first, last); + + return _command(fmt::hmget, key, first, last); + } + + template + Future hmget(const StringView &key, std::initializer_list il) { + return hmget(key, il.begin(), il.end()); + } + + template + Future hmset(const StringView &key, Input first, Input last) { + range_check("HMSET", first, last); + + return _command(fmt::hmset, key, first, last); + } + + template + Future hmset(const StringView &key, std::initializer_list il) { + return hmset(key, il.begin(), il.end()); + } + + Future hset(const StringView &key, const StringView &field, const StringView &val) { + return _command(fmt::hset, key, field, val); + } + + Future hset(const StringView &key, const std::pair &item) { + return hset(key, item.first, item.second); + } + + template + auto hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, Future>::type { + range_check("HSET", first, last); + + return _command(fmt::hset_range, key, first, last); + } + + template + Future hset(const StringView &key, std::initializer_list il) { + return hset(key, il.begin(), il.end()); + } + + template + Future hvals(const StringView &key) { + return _command(fmt::hvals, key); + } + + // SET commands. + + Future sadd(const StringView &key, const StringView &member) { + return _command(fmt::sadd, key, member); + } + + template + Future sadd(const StringView &key, Input first, Input last) { + range_check("SADD", first, last); + + return _command(fmt::sadd_range, key, first, last); + } + + template + Future sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + Future scard(const StringView &key) { + return _command(fmt::scard, key); + } + + Future sismember(const StringView &key, const StringView &member) { + return _command(fmt::sismember, key, member); + } + + template + Future smembers(const StringView &key) { + return _command(fmt::smembers, key); + } + + Future spop(const StringView &key) { + return _command(fmt::spop, key); + } + + template + Future spop(const StringView &key, long long count) { + return _command(fmt::spop, key, count); + } + + Future srem(const StringView &key, const StringView &member) { + return _command(fmt::srem, key, member); + } + + template + Future srem(const StringView &key, Input first, Input last) { + range_check("SREM", first, last); + + return _command(fmt::srem_range, key, first, last); + } + + template + Future srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + // SORTED SET commands. + + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + return _command>>( + fmt::bzpopmax, key, timeout); + } + + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + range_check("BZPOPMAX", first, last); + + return _command>>( + fmt::bzpopmax_range, first, last, timeout); + } + + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + return _command>>( + fmt::bzpopmin, key, timeout); + } + + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + range_check("BZPOPMIN", first, last); + + return _command>>( + fmt::bzpopmin_range, first, last, timeout); + } + + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + Future zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return _command(fmt::zadd, key, member, score, type, changed); + } + + template + Future zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + range_check("ZADD", first, last); + + return _command(fmt::zadd_range, + key, + first, + last, + type, + changed); + } + + template + Future zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + Future zcard(const StringView &key) { + return _command(fmt::zcard, key); + } + + template + Future zcount(const StringView &key, const Interval &interval) { + return _command(fmt::zcount, key, interval); + } + + Future zincrby(const StringView &key, double increment, const StringView &member) { + return _command(fmt::zincrby, key, increment, member); + } + + template + Future zlexcount(const StringView &key, const Interval &interval) { + return _command(fmt::zlexcount, key, interval); + } + + Future>> zpopmax(const StringView &key) { + return _command>, + FormattedCommand (*)(const StringView &)>(fmt::zpopmax, key); + } + + template + Future zpopmax(const StringView &key, long long count) { + return _command( + fmt::zpopmax, key, count); + } + + Future>> zpopmin(const StringView &key) { + return _command>, + FormattedCommand (*)(const StringView &)>(fmt::zpopmin, key); + } + + template + Future zpopmin(const StringView &key, long long count) { + return _command( + fmt::zpopmin, key, count); + } + + template + Future zrange(const StringView &key, long long start, long long stop) { + return _command(fmt::zrange, key, start, stop); + } + + template + Future zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return _command(fmt::zrangebylex, + key, interval, opts); + } + + template + Future zrangebylex(const StringView &key, const Interval &interval) { + return zrangebylex(key, interval, {}); + } + + // TODO: withscores parameter + template + Future zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return _command(fmt::zrangebyscore, + key, interval, opts); + } + + template + Future zrangebyscore(const StringView &key, const Interval &interval) { + return zrangebyscore(key, interval, {}); + } + + Future zrank(const StringView &key, const StringView &member) { + return _command(fmt::zrank, key, member); + } + + Future zrem(const StringView &key, const StringView &member) { + return _command(fmt::zrem, key, member); + } + + template + Future zrem(const StringView &key, Input first, Input last) { + range_check("ZREM", first, last); + + return _command(fmt::zrem_range, key, first, last); + } + + template + Future zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + Future zremrangebylex(const StringView &key, const Interval &interval) { + return _command(fmt::zremrangebylex, key, interval); + } + + Future zremrangebyrank(const StringView &key, long long start, long long stop) { + return _command(fmt::zremrangebyrank, key, start, stop); + } + + template + Future zremrangebyscore(const StringView &key, const Interval &interval) { + return _command(fmt::zremrangebyscore, key, interval); + } + + template + Future zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return _command(fmt::zrevrangebylex, + key, interval, opts); + } + + template + Future zrevrangebylex(const StringView &key, const Interval &interval) { + return zrevrangebylex(key, interval, {}); + } + + Future zrevrank(const StringView &key, const StringView &member) { + return _command(fmt::zrevrank, key, member); + } + + Future zscore(const StringView &key, const StringView &member) { + return _command(fmt::zscore, key, member); + } + + // SCRIPTING commands. + + template + Future eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + return _command(fmt::eval, + script, keys_first, keys_last, args_first, args_last); + } + + template + Future eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return eval(script, + keys.begin(), keys.end(), + args.begin(), args.end()); + } + + template + Future evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + return _command(fmt::evalsha, + script, keys_first, keys_last, args_first, args_last); + } + + template + Future evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return evalsha(script, + keys.begin(), keys.end(), + args.begin(), args.end()); + } + +private: + template + Future _command(Formatter formatter, Args &&...args) { + return _command_with_parser>( + formatter, std::forward(args)...); + } + + template + Future _command_with_parser(Formatter formatter, Args &&...args) { + auto formatted_cmd = formatter(std::forward(args)...); + + assert(_pool); + SafeAsyncConnection connection(*_pool); + + return connection.connection().send(std::move(formatted_cmd)); + } + + EventLoopSPtr _loop; + + AsyncConnectionPoolSPtr _pool; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ASYNC_REDIS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis_cluster.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis_cluster.cpp new file mode 100644 index 000000000..d7ae2dee3 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis_cluster.cpp @@ -0,0 +1,37 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "async_redis_cluster.h" +#include + +namespace sw { + +namespace redis { + +AsyncRedisCluster::AsyncRedisCluster(const ConnectionOptions &opts, + const ConnectionPoolOptions &pool_opts, + Role role, + const EventLoopSPtr &loop) : _loop(loop) { + if (!_loop) { + _loop = std::make_shared(); + } + + _pool = std::make_shared(_loop, pool_opts, opts, role); +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis_cluster.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis_cluster.h new file mode 100644 index 000000000..b18be94ea --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_redis_cluster.h @@ -0,0 +1,819 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ASYNC_REDIS_CLUSTER_H +#define SEWENEW_REDISPLUSPLUS_ASYNC_REDIS_CLUSTER_H + +#include +#include "utils.h" +#include "async_connection.h" +#include "async_connection_pool.h" +#include "async_shards_pool.h" +#include "event_loop.h" +#include "cmd_formatter.h" + +namespace sw { + +namespace redis { + +class AsyncRedisCluster { +public: + AsyncRedisCluster(const ConnectionOptions &opts, + const ConnectionPoolOptions &pool_opts = {}, + Role role = Role::MASTER, + const EventLoopSPtr &loop = nullptr); + + AsyncRedisCluster(const AsyncRedisCluster &) = delete; + AsyncRedisCluster& operator=(const AsyncRedisCluster &) = delete; + + AsyncRedisCluster(AsyncRedisCluster &&) = default; + AsyncRedisCluster& operator=(AsyncRedisCluster &&) = default; + + ~AsyncRedisCluster() = default; + + template + Future command(const StringView &cmd_name, const StringView &key, Args &&...args) { + auto formatter = [&cmd_name](const StringView &key, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, key, std::forward(args)...); + return fmt::format_cmd(cmd_args); + }; + + return _command(formatter, key, std::forward(args)...); + } + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Future>::type { + if (first == last || std::next(first) == last) { + throw Error("command: invalid range"); + } + + const auto &cmd_name = *first; + ++first; + + auto formatter = [&cmd_name](Input first, Input last) { + CmdArgs cmd_args; + cmd_args.append(cmd_name); + while (first != last) { + cmd_args.append(*first); + ++first; + } + return fmt::format_cmd(cmd_args); + }; + + return _command(formatter, first, last); + } + + // CONNECTION commands. + + Future del(const StringView &key) { + return _command(fmt::del, key); + } + + template + Future del(Input first, Input last) { + range_check("DEL", first, last); + + return _command(fmt::del_range, first, last); + } + + template + Future del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + Future exists(const StringView &key) { + return _command(fmt::exists, key); + } + + template + Future exists(Input first, Input last) { + range_check("EXISTS", first, last); + + return _command(fmt::exists_range, first, last); + } + + template + Future exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + Future expire(const StringView &key, const std::chrono::seconds &timeout) { + return _command(fmt::expire, key, timeout); + } + + Future expireat(const StringView &key, + const std::chrono::time_point &tp) { + return _command(fmt::expireat, key, tp); + } + + Future pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return _command(fmt::pexpire, key, timeout); + } + + Future pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return _command(fmt::pexpireat, key, tp); + } + + Future pttl(const StringView &key) { + return _command(fmt::pttl, key); + } + + Future rename(const StringView &key, const StringView &newkey) { + return _command(fmt::rename, key, newkey); + } + + Future renamenx(const StringView &key, const StringView &newkey) { + return _command(fmt::renamenx, key, newkey); + } + + Future ttl(const StringView &key) { + return _command(fmt::ttl, key); + } + + Future unlink(const StringView &key) { + return _command(fmt::unlink, key); + } + + template + Future unlink(Input first, Input last) { + range_check("UNLINK", first, last); + + return _command(fmt::unlink_range, first, last); + } + + template + Future unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + // STRING commands. + + Future get(const StringView &key) { + return _command(fmt::get, key); + } + + Future incr(const StringView &key) { + return _command(fmt::incr, key); + } + + Future incrby(const StringView &key, long long increment) { + return _command(fmt::incrby, key, increment); + } + + Future incrbyfloat(const StringView &key, double increment) { + return _command(fmt::incrbyfloat, key, increment); + } + + template + Future mget(Input first, Input last) { + range_check("MGET", first, last); + + return _command(fmt::mget, first, last); + } + + template + Future mget(std::initializer_list il) { + return mget(il.begin(), il.end()); + } + + template + Future mset(Input first, Input last) { + range_check("MSET", first, last); + + return _command(fmt::mset, first, last); + } + + template + Future mset(std::initializer_list il) { + return mset(il.begin(), il.end()); + } + + template + Future msetnx(Input first, Input last) { + range_check("MSETNX", first, last); + + return _command(fmt::msetnx, first, last); + } + + template + Future msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + Future set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS) { + return _command_with_parser(fmt::set, + key, key, val, ttl, type); + } + + Future strlen(const StringView &key) { + return _command(fmt::strlen, key); + } + + // LIST commands. + + Future blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return _command(fmt::blpop, key, timeout); + } + + template + Future blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + range_check("BLPOP", first, last); + + return _command(fmt::blpop_range, first, last, timeout); + } + + template + Future blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + Future brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return _command(fmt::brpop, key, timeout); + } + + template + Future brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + range_check("BRPOP", first, last); + + return _command(fmt::brpop_range, first, last, timeout); + } + + template + Future brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + Future brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return _command(fmt::brpoplpush, source, destination, timeout); + } + + Future llen(const StringView &key) { + return _command(fmt::llen, key); + } + + Future lpop(const StringView &key) { + return _command(fmt::lpop, key); + } + + Future lpush(const StringView &key, const StringView &val) { + return _command(fmt::lpush, key, val); + } + + template + Future lpush(const StringView &key, Input first, Input last) { + range_check("LPUSH", first, last); + + return _command(fmt::lpush_range, key, first, last); + } + + template + Future lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + template + Future lrange(const StringView &key, long long start, long long stop) { + return _command(fmt::lrange, key, start, stop); + } + + Future lrem(const StringView &key, long long count, const StringView &val) { + return _command(fmt::lrem, key, count, val); + } + + Future ltrim(const StringView &key, long long start, long long stop) { + return _command(fmt::ltrim, key, start, stop); + } + + Future rpop(const StringView &key) { + return _command(fmt::rpop, key); + } + + Future rpoplpush(const StringView &source, const StringView &destination) { + return _command(fmt::rpoplpush, source, destination); + } + + Future rpush(const StringView &key, const StringView &val) { + return _command(fmt::rpush, key, val); + } + + template + Future rpush(const StringView &key, Input first, Input last) { + range_check("RPUSH", first, last); + + return _command(fmt::rpush_range, key, first, last); + } + + template + Future rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + // HASH commands. + + Future hdel(const StringView &key, const StringView &field) { + return _command(fmt::hdel, key, field); + } + + template + Future hdel(const StringView &key, Input first, Input last) { + range_check("HDEL", first, last); + + return _command(fmt::hdel_range, key, first, last); + } + + template + Future hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + Future hexists(const StringView &key, const StringView &field) { + return _command(fmt::hexists, key, field); + } + + Future hget(const StringView &key, const StringView &field) { + return _command(fmt::hget, key, field); + } + + template + Future hgetall(const StringView &key) { + return _command(fmt::hgetall, key); + } + + Future hincrby(const StringView &key, const StringView &field, long long increment) { + return _command(fmt::hincrby, key, field, increment); + } + + Future hincrbyfloat(const StringView &key, const StringView &field, double increment) { + return _command(fmt::hincrbyfloat, key, field, increment); + } + + template + Future hkeys(const StringView &key) { + return _command(fmt::hkeys, key); + } + + Future hlen(const StringView &key) { + return _command(fmt::hlen, key); + } + + template + Future hmget(const StringView &key, Input first, Input last) { + range_check("HMGET", first, last); + + return _command(fmt::hmget, key, first, last); + } + + template + Future hmget(const StringView &key, std::initializer_list il) { + return hmget(key, il.begin(), il.end()); + } + + template + Future hmset(const StringView &key, Input first, Input last) { + range_check("HMSET", first, last); + + return _command(fmt::hmset, key, first, last); + } + + template + Future hmset(const StringView &key, std::initializer_list il) { + return hmset(key, il.begin(), il.end()); + } + + Future hset(const StringView &key, const StringView &field, const StringView &val) { + return _command(fmt::hset, key, field, val); + } + + Future hset(const StringView &key, const std::pair &item) { + return hset(key, item.first, item.second); + } + + template + auto hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, Future>::type { + range_check("HSET", first, last); + + return _command(fmt::hset_range, key, first, last); + } + + template + Future hset(const StringView &key, std::initializer_list il) { + return hset(key, il.begin(), il.end()); + } + + template + Future hvals(const StringView &key) { + return _command(fmt::hvals, key); + } + + // SET commands. + + Future sadd(const StringView &key, const StringView &member) { + return _command(fmt::sadd, key, member); + } + + template + Future sadd(const StringView &key, Input first, Input last) { + range_check("SADD", first, last); + + return _command(fmt::sadd_range, key, first, last); + } + + template + Future sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + Future scard(const StringView &key) { + return _command(fmt::scard, key); + } + + Future sismember(const StringView &key, const StringView &member) { + return _command(fmt::sismember, key, member); + } + + template + Future smembers(const StringView &key) { + return _command(fmt::smembers, key); + } + + Future spop(const StringView &key) { + return _command(fmt::spop, key); + } + + template + Future spop(const StringView &key, long long count) { + return _command(fmt::spop, key, count); + } + + Future srem(const StringView &key, const StringView &member) { + return _command(fmt::srem, key, member); + } + + template + Future srem(const StringView &key, Input first, Input last) { + range_check("SREM", first, last); + + return _command(fmt::srem_range, key, first, last); + } + + template + Future srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + // SORTED SET commands. + + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + return _command>>( + fmt::bzpopmax, key, timeout); + } + + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + range_check("BZPOPMAX", first, last); + + return _command>>( + fmt::bzpopmax_range, first, last, timeout); + } + + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + return _command>>( + fmt::bzpopmin, key, timeout); + } + + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + range_check("BZPOPMIN", first, last); + + return _command>>( + fmt::bzpopmin_range, first, last, timeout); + } + + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Future>> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + Future zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return _command(fmt::zadd, key, member, score, type, changed); + } + + template + Future zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + range_check("ZADD", first, last); + + return _command(fmt::zadd_range, key, first, last, type, changed); + } + + template + Future zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + Future zcard(const StringView &key) { + return _command(fmt::zcard, key); + } + + template + Future zcount(const StringView &key, const Interval &interval) { + return _command(fmt::zcount, key, interval); + } + + Future zincrby(const StringView &key, double increment, const StringView &member) { + return _command(fmt::zincrby, key, increment, member); + } + + template + Future zlexcount(const StringView &key, const Interval &interval) { + return _command(fmt::zlexcount, key, interval); + } + + Future>> zpopmax(const StringView &key) { + return _command>, + FormattedCommand (*)(const StringView &)>(fmt::zpopmax, key); + } + + template + Future zpopmax(const StringView &key, long long count) { + return _command( + fmt::zpopmax, key, count); + } + + Future>> zpopmin(const StringView &key) { + return _command>, + FormattedCommand (*)(const StringView &)>(fmt::zpopmin, key); + } + + template + Future zpopmin(const StringView &key, long long count) { + return _command( + fmt::zpopmin, key, count); + } + + template + Future zrange(const StringView &key, long long start, long long stop) { + return _command(fmt::zrange, key, start, stop); + } + + template + Future zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return _command(fmt::zrangebylex, key, interval, opts); + } + + template + Future zrangebylex(const StringView &key, const Interval &interval) { + return zrangebylex(key, interval, {}); + } + + template + Future zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return _command(fmt::zrangebyscore, + key, interval, opts); + } + + template + Future zrangebyscore(const StringView &key, const Interval &interval) { + return zrangebyscore(key, interval, {}); + } + + Future zrank(const StringView &key, const StringView &member) { + return _command(fmt::zrank, key, member); + } + + Future zrem(const StringView &key, const StringView &member) { + return _command(fmt::zrem, key, member); + } + + template + Future zrem(const StringView &key, Input first, Input last) { + range_check("ZREM", first, last); + + return _command(fmt::zrem_range, key, first, last); + } + + template + Future zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + Future zremrangebylex(const StringView &key, const Interval &interval) { + return _command(fmt::zremrangebylex, key, interval); + } + + Future zremrangebyrank(const StringView &key, long long start, long long stop) { + return _command(fmt::zremrangebyrank, key, start, stop); + } + + template + Future zremrangebyscore(const StringView &key, const Interval &interval) { + return _command(fmt::zremrangebyscore, key, interval); + } + + template + Future zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return _command(fmt::zrevrangebylex, key, interval, opts); + } + + template + Future zrevrangebylex(const StringView &key, const Interval &interval) { + return zrevrangebylex(key, interval, {}); + } + + Future zrevrank(const StringView &key, const StringView &member) { + return _command(fmt::zrevrank, key, member); + } + + Future zscore(const StringView &key, const StringView &member) { + return _command(fmt::zscore, key, member); + } + + // SCRIPTING commands. + + template + Future eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + return _generic_command(fmt::eval, *keys_first, script, + keys_first, keys_last, + args_first, args_last); + } + + template + Future eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return eval(script, + keys.begin(), keys.end(), + args.begin(), args.end()); + } + + template + Future evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + return _generic_command(fmt::evalsha, *keys_first, script, + keys_first, keys_last, args_first, args_last); + } + + template + Future evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return evalsha(script, + keys.begin(), keys.end(), + args.begin(), args.end()); + } + +private: + template + Future _command_with_parser(Formatter formatter, + const StringView &key, Args &&...args) { + auto formatted_cmd = formatter(std::forward(args)...); + + assert(_pool); + + auto pool = _pool->fetch(key); + assert(pool); + + GuardedAsyncConnection connection(pool); + + return connection.connection().send( + _pool, key, std::move(formatted_cmd)); + } + + template + Future _generic_command(Formatter formatter, const StringView &key, Args &&...args) { + return _command_with_parser>( + formatter, key, std::forward(args)...); + } + + template + Future _command(Formatter formatter, Key &&key, Args &&...args) { + return _generic_command(formatter, + std::is_convertible::type, StringView>(), + std::forward(key), + std::forward(args)...); + } + + template + Future _generic_command(Formatter formatter, std::true_type, + const StringView &key, Args &&...args) { + return _generic_command(formatter, key, key, std::forward(args)...); + } + + template + Future _generic_command(Formatter formatter, std::false_type, Input &&input, Args &&...args) { + return _range_command(formatter, + std::is_convertible())>::type, StringView>(), + std::forward(input), + std::forward(args)...); + } + + template + Future _range_command(Formatter formatter, std::true_type, + Input &&input, Args &&...args) { + return _generic_command(formatter, *input, + std::forward(input), std::forward(args)...); + } + + template + Future _range_command(Formatter formatter, std::false_type, + Input &&input, Args &&...args) { + return _generic_command(formatter, std::get<0>(*input), + std::forward(input), std::forward(args)...); + } + + EventLoopSPtr _loop; + + AsyncShardsPoolSPtr _pool; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ASYNC_REDIS_CLUSTER_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_sentinel.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_sentinel.cpp new file mode 100644 index 000000000..3ea3e49de --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_sentinel.cpp @@ -0,0 +1,110 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "async_sentinel.h" +#include +#include "errors.h" +#include "async_connection_pool.h" + +namespace sw { + +namespace redis { + +AsyncSentinel::AsyncSentinel(const SentinelOptions &sentinel_opts) : + _sentinel(std::make_shared(sentinel_opts)), + _worker([this]() { this->_run(); }) {} + +AsyncSentinel::~AsyncSentinel() { + _stop_worker(); + + if (_worker.joinable()) { + _worker.join(); + } +} + +void AsyncSentinel::add(AsyncSentinelTask task) { + { + std::lock_guard lock(_mutex); + + _tasks.push(std::move(task)); + } + + _cv.notify_one(); +} + +void AsyncSentinel::_stop_worker() { + // Add an empty task to stop the worker thread. + add(AsyncSentinelTask{}); +} + +void AsyncSentinel::_run() { + while (true) { + auto tasks = _fetch_tasks(); + + assert(!tasks.empty()); + + while (!tasks.empty()) { + auto &task = tasks.front(); + if (!task) { + // Got a stopping task. + return; + } + + _run_task(task); + + tasks.pop(); + } + } +} + +void AsyncSentinel::_run_task(AsyncSentinelTask &task) { + auto pool = task.pool.lock(); + if (!pool) { + // AsyncConnectionPool has been destroyed, give up the task. + return; + } + + auto &connection = task.connection; + try { + SimpleSentinel sentinel(_sentinel, task.master_name, task.role); + auto sync_connection = sentinel.create(connection->options()); + + const auto &opts = sync_connection.options(); + pool->update_node_info(opts.host, opts.port, connection); + } catch (const StopIterError &e) { + pool->update_node_info(connection, + std::make_exception_ptr(Error("Failed to create connection with sentinel"))); + } catch (const Error &e) { + pool->update_node_info(connection, std::current_exception()); + } +} + +auto AsyncSentinel::_fetch_tasks() -> std::queue { + std::queue tasks; + + std::unique_lock lock(_mutex); + if (_tasks.empty()) { + _cv.wait(lock, [this]() { return !(this->_tasks).empty(); } ); + } + + tasks.swap(_tasks); + + return tasks; +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_sentinel.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_sentinel.h new file mode 100644 index 000000000..7b1a3463d --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_sentinel.h @@ -0,0 +1,89 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ASYNC_SENTINEL_H +#define SEWENEW_REDISPLUSPLUS_ASYNC_SENTINEL_H + +#include +#include +#include +#include +#include "sentinel.h" +#include "async_connection.h" + +namespace sw { + +namespace redis { + +class AsyncConnectionPool; + +class AsyncSentinel { +public: + explicit AsyncSentinel(const SentinelOptions &sentinel_opts); + + AsyncSentinel(const AsyncSentinel &) = delete; + AsyncSentinel& operator=(const AsyncSentinel &) = delete; + + AsyncSentinel(AsyncSentinel &&) = delete; + AsyncSentinel& operator=(AsyncSentinel &&) = delete; + + ~AsyncSentinel(); + +private: + friend class SimpleAsyncSentinel; + + struct AsyncSentinelTask { + operator bool() const noexcept { + return bool(connection); + } + + std::weak_ptr pool; + + AsyncConnectionSPtr connection; + + std::string master_name; + + Role role = Role::MASTER; + }; + + void add(AsyncSentinelTask task); + + void _stop_worker(); + + void _run_task(AsyncSentinelTask &task); + + std::queue _fetch_tasks(); + + void _run(); + + std::queue _tasks; + + std::shared_ptr _sentinel; + + std::thread _worker; + + std::mutex _mutex; + + std::condition_variable _cv; +}; + +using AsyncSentinelSPtr = std::shared_ptr; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ASYNC_SENTINEL_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_shards_pool.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_shards_pool.cpp new file mode 100644 index 000000000..46c5b8bf2 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_shards_pool.cpp @@ -0,0 +1,374 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "async_shards_pool.h" +#include +#include "errors.h" + +namespace sw { + +namespace redis { + +const std::size_t AsyncShardsPool::SHARDS; + +AsyncShardsPool::AsyncShardsPool(const EventLoopSPtr &loop, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts, + Role role) : + _pool_opts(pool_opts), + _connection_opts(connection_opts), + _role(role), + _loop(loop) { + assert(_loop); + + if (_connection_opts.type != ConnectionType::TCP) { + throw Error("Only support TCP connection for Redis Cluster"); + } + + auto node = Node{_connection_opts.host, _connection_opts.port}; + _shards.emplace(SlotRange{0U, SHARDS}, node); + _pools.emplace(node, + std::make_shared(_loop, _pool_opts, _connection_opts)); + + _worker = std::thread([this]() { this->_run(); }); +} + +AsyncShardsPool::AsyncShardsPool(AsyncShardsPool &&that) { + std::lock_guard lock(that._mutex); + + _move(std::move(that)); +} + +AsyncShardsPool& AsyncShardsPool::operator=(AsyncShardsPool &&that) { + if (this != &that) { + std::lock(_mutex, that._mutex); + std::lock_guard lock_this(_mutex, std::adopt_lock); + std::lock_guard lock_that(that._mutex, std::adopt_lock); + + _move(std::move(that)); + } + + return *this; +} + +AsyncShardsPool::~AsyncShardsPool() { + update({}, nullptr); + + if (_worker.joinable()) { + _worker.join(); + } +} + +AsyncConnectionPoolSPtr AsyncShardsPool::fetch(const StringView &key) { + auto slot = _slot(key); + + return _fetch(slot); +} + +AsyncConnectionPoolSPtr AsyncShardsPool::fetch() { + auto slot = _slot(); + + return _fetch(slot); +} + +AsyncConnectionPoolSPtr AsyncShardsPool::fetch(const Node &node) { + std::lock_guard lock(_mutex); + + auto iter = _pools.find(node); + if (iter == _pools.end()) { + // Node doesn't exist, and it should be a newly created node. + // So add a new connection pool. + iter = _add_node(node); + } + + assert(iter != _pools.end()); + + return iter->second; +} + +void AsyncShardsPool::update(const std::string &key, AsyncEventUPtr event) { + { + std::lock_guard lock(_mutex); + + _events.push(RedeliverEvent{key, std::move(event)}); + } + + _cv.notify_one(); +} + +ConnectionOptions AsyncShardsPool::connection_options(const StringView &key) { + auto slot = _slot(key); + + return _connection_options(slot); +} + +ConnectionOptions AsyncShardsPool::connection_options() { + auto slot = _slot(); + + return _connection_options(slot); +} + +void AsyncShardsPool::_move(AsyncShardsPool &&that) { + _pool_opts = that._pool_opts; + _connection_opts = that._connection_opts; + _role = that._role; + _shards = std::move(that._shards); + _pools = std::move(that._pools); + _loop = std::move(that._loop); + _worker = std::move(that._worker); + _events = std::move(that._events); +} + +ConnectionOptions AsyncShardsPool::_connection_options(Slot slot) { + std::lock_guard lock(_mutex); + + auto &pool = _get_pool(slot); + + assert(pool); + + return pool->connection_options(); +} + +Slot AsyncShardsPool::_slot(const StringView &key) const { + // The following code is copied from: https://redis.io/topics/cluster-spec + // And I did some minor changes. + + const auto *k = key.data(); + auto keylen = key.size(); + + // start-end indexes of { and }. + std::size_t s = 0; + std::size_t e = 0; + + // Search the first occurrence of '{'. + for (s = 0; s < keylen; s++) + if (k[s] == '{') break; + + // No '{' ? Hash the whole key. This is the base case. + if (s == keylen) return crc16(k, keylen) & SHARDS; + + // '{' found? Check if we have the corresponding '}'. + for (e = s + 1; e < keylen; e++) + if (k[e] == '}') break; + + // No '}' or nothing between {} ? Hash the whole key. + if (e == keylen || e == s + 1) return crc16(k, keylen) & SHARDS; + + // If we are here there is both a { and a } on its right. Hash + // what is in the middle between { and }. + return crc16(k + s + 1, e - s - 1) & SHARDS; +} + +Slot AsyncShardsPool::_slot() const { + return _random(0, SHARDS); +} + +AsyncConnectionPoolSPtr AsyncShardsPool::_fetch(Slot slot) { + std::lock_guard lock(_mutex); + + return _get_pool(slot); +} + +AsyncConnectionPoolSPtr& AsyncShardsPool::_get_pool(Slot slot) { + const auto &node = _get_node(slot); + + auto node_iter = _pools.find(node); + if (node_iter == _pools.end()) { + throw Error("Slot is NOT covered: " + std::to_string(slot)); + } + + return node_iter->second; +} + +void AsyncShardsPool::_run() { + while (true) { + auto events = _fetch_events(); + + assert(!events.empty()); + + try { + // TODO: when we try to stop the worker thread, + // we don't need to call `_update_shards` + _update_shards(); + + // if _redeliver_events or _fail_events returns true if there's a null event, + // and we exit the thread loop. + if (_redeliver_events(events)) { + break; + } + } catch (...) { + if (_fail_events(events, std::current_exception())) { + break; + } + } + } +} + +auto AsyncShardsPool::_fetch_events() -> std::queue { + std::queue events; + + std::unique_lock lock(_mutex); + if (_events.empty()) { + _cv.wait(lock, [this]() { return !(this->_events).empty(); } ); + } + + events.swap(_events); + + return events; +} + +std::size_t AsyncShardsPool::_random(std::size_t min, std::size_t max) const { + static thread_local std::default_random_engine engine; + + std::uniform_int_distribution uniform_dist(min, max); + + return uniform_dist(engine); +} + +const Node& AsyncShardsPool::_get_node(Slot slot) const { + auto shards_iter = _shards.lower_bound(SlotRange{slot, slot}); + if (shards_iter == _shards.end() || slot < shards_iter->first.min) { + throw Error("Slot is out of range: " + std::to_string(slot)); + } + + return shards_iter->second; +} + +Shards AsyncShardsPool::_get_shards(const std::string &host, int port) { + auto opts = _connection_opts; + opts.host = host; + opts.port = port; + ShardsPool pool(_pool_opts, opts, _role); + + return pool.shards(); +} + +void AsyncShardsPool::_update_shards() { + for (int idx = 0; idx < 4; ++idx) { + try { + Shards shards; + if (idx < 3) { + Node node; + { + std::lock_guard lock(_mutex); + + // Randomly pick a node. + node = _get_node(_slot()); + } + + shards = _get_shards(node.host, node.port); + } else { + shards = _get_shards(_connection_opts.host, _connection_opts.port); + } + + std::unordered_set nodes; + for (const auto &shard : shards) { + nodes.insert(shard.second); + } + + std::lock_guard lock(_mutex); + + _shards = std::move(shards); + + // Remove non-existent nodes. + for (auto iter = _pools.begin(); iter != _pools.end(); ) { + if (nodes.find(iter->first) == nodes.end()) { + // Node has been removed. + _pools.erase(iter++); + } else { + ++iter; + } + } + + // Add connection pool for new nodes. + // In fact, connections will be created lazily. + for (const auto &node : nodes) { + if (_pools.find(node) == _pools.end()) { + _add_node(node); + } + } + + // Update successfully. + return; + } catch (const Error &e) { + // continue; + } + } + + throw Error("Failed to update shards info"); +} + +auto AsyncShardsPool::_add_node(const Node &node) -> NodeMap::iterator { + auto opts = _connection_opts; + opts.host = node.host; + opts.port = node.port; + + // TODO: Better set readonly an attribute of `Node`. + if (_role == Role::SLAVE) { + opts.readonly = true; + } + + return _pools.emplace(node, + std::make_shared(_loop, _pool_opts, opts)).first; +} + +bool AsyncShardsPool::_redeliver_events(std::queue &events) { + bool should_stop_worker = false; + while (!events.empty()) { + auto &event = events.front(); + try { + auto &async_event = event.event; + if (!async_event) { + should_stop_worker = true; + events.pop(); + continue; + } + + auto pool = fetch(event.key); + assert(pool); + + GuardedAsyncConnection connection(pool); + + connection.connection().send(std::move(async_event)); + } catch (...) { + event.event->set_exception(std::current_exception()); + } + events.pop(); + } + + return should_stop_worker; +} + +bool AsyncShardsPool::_fail_events(std::queue &events, + std::exception_ptr err) { + bool should_stop_worker = false; + while (!events.empty()) { + auto &event = events.front(); + auto &async_event = event.event; + if (!async_event) { + should_stop_worker = true; + } else { + async_event->set_exception(err); + } + events.pop(); + } + + return should_stop_worker; +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_shards_pool.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_shards_pool.h new file mode 100644 index 000000000..bf3641acd --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/async_shards_pool.h @@ -0,0 +1,127 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ASYNC_SHARDS_POOL_H +#define SEWENEW_REDISPLUSPLUS_ASYNC_SHARDS_POOL_H + +#include +#include +#include +#include +#include +#include +#include "shards_pool.h" +#include "async_connection_pool.h" + +namespace sw { + +namespace redis { + +class AsyncShardsPool { +public: + AsyncShardsPool(const AsyncShardsPool &) = delete; + AsyncShardsPool& operator=(const AsyncShardsPool &) = delete; + + AsyncShardsPool(AsyncShardsPool &&that); + AsyncShardsPool& operator=(AsyncShardsPool &&that); + + ~AsyncShardsPool(); + + AsyncShardsPool(const EventLoopSPtr &loop, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts, + Role role); + + AsyncConnectionPoolSPtr fetch(const StringView &key); + + AsyncConnectionPoolSPtr fetch(); + + AsyncConnectionPoolSPtr fetch(const Node &node); + + void update(const std::string &key, AsyncEventUPtr event); + + ConnectionOptions connection_options(const StringView &key); + + ConnectionOptions connection_options(); + +private: + struct RedeliverEvent { + std::string key; + AsyncEventUPtr event; + }; + + void _run(); + + void _move(AsyncShardsPool &&that); + + Slot _slot(const StringView &key) const; + + AsyncConnectionPoolSPtr _fetch(Slot slot); + + Slot _slot() const; + + std::queue _fetch_events(); + + void _update_shards(); + + bool _redeliver_events(std::queue &events); + + bool _fail_events(std::queue &events, std::exception_ptr err); + + using NodeMap = std::unordered_map; + + NodeMap::iterator _add_node(const Node &node); + + std::size_t _random(std::size_t min, std::size_t max) const; + + const Node& _get_node(Slot slot) const; + + AsyncConnectionPoolSPtr& _get_pool(Slot slot); + + ConnectionOptions _connection_options(Slot slot); + + Shards _get_shards(const std::string &host, int port); + + ConnectionPoolOptions _pool_opts; + + ConnectionOptions _connection_opts; + + Role _role = Role::MASTER; + + Shards _shards; + + NodeMap _pools; + + EventLoopSPtr _loop; + + std::thread _worker; + + std::condition_variable _cv; + + std::mutex _mutex; + + std::queue _events; + + static const std::size_t SHARDS = 16383; +}; + +using AsyncShardsPoolSPtr = std::shared_ptr; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ASYNC_SHARDS_POOL_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/cmd_formatter.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/cmd_formatter.h new file mode 100644 index 000000000..85efdf347 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/cmd_formatter.h @@ -0,0 +1,775 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CMD_FORMATTER_H +#define SEWENEW_REDISPLUSPLUS_CMD_FORMATTER_H + +#include +#include "command_options.h" +#include "command_args.h" +#include "command.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class FormattedCommand { +public: + FormattedCommand(char *data, int len) : _data(data), _size(len) { + if (data == nullptr || len < 0) { + throw Error("failed to format command"); + } + } + + FormattedCommand(const FormattedCommand &) = delete; + FormattedCommand& operator=(const FormattedCommand &) = delete; + + FormattedCommand(FormattedCommand &&that) noexcept { + _move(std::move(that)); + } + + FormattedCommand& operator=(FormattedCommand &&that) noexcept { + if (this != &that) { + _move(std::move(that)); + } + + return *this; + } + + ~FormattedCommand() noexcept { + if (_data != nullptr) { + redisFreeCommand(_data); + } + } + + const char* data() const noexcept { + return _data; + } + + int size() const noexcept { + return _size; + } + +private: + void _move(FormattedCommand &&that) noexcept { + _data = that._data; + _size = that._size; + that._data = nullptr; + that._size = 0; + } + + char *_data = nullptr; + int _size = 0; +}; + +namespace fmt { + +template +FormattedCommand format_cmd(const char *format, Args &&...args) { + char *data = nullptr; + auto len = redisFormatCommand(&data, format, std::forward(args)...); + + return FormattedCommand(data, len); +} + +inline FormattedCommand format_cmd(int argc, const char **argv, const std::size_t *argv_len) { + char *data = nullptr; + auto len = redisFormatCommandArgv(&data, argc, argv, argv_len); + + return FormattedCommand(data, len); +} + +inline FormattedCommand format_cmd(CmdArgs &args) { + char *data = nullptr; + auto len = redisFormatCommandArgv(&data, args.size(), args.argv(), args.argv_len()); + + return FormattedCommand(data, len); +} + +struct SetResultParser { + bool operator()(redisReply &reply) const { + sw::redis::reply::rewrite_set_reply(reply); + return sw::redis::reply::parse(reply); + } +}; + +// CONNECTION commands. + +inline FormattedCommand echo(const StringView &msg) { + return format_cmd("ECHO %b", msg.data(), msg.size()); +} + +inline FormattedCommand ping() { + return format_cmd("PING"); +} + +inline FormattedCommand ping(const StringView &msg) { + return format_cmd("PING %b", msg.data(), msg.size()); +} + +inline FormattedCommand del(const StringView &key) { + return format_cmd("DEL %b", key.data(), key.size()); +} + +template +FormattedCommand del_range(Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "DEL" << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand exists(const StringView &key) { + return format_cmd("EXISTS %b", key.data(), key.size()); +} + +template +FormattedCommand exists_range(Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "EXISTS" << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand expire(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("EXPIRE %b %lld", key.data(), key.size(), timeout.count()); +} + +inline FormattedCommand expireat(const StringView &key, + const std::chrono::time_point &tp) { + return format_cmd("EXPIREAT %b %lld", key.data(), key.size(), tp.time_since_epoch().count()); +} + +inline FormattedCommand pexpire(const StringView &key, + const std::chrono::milliseconds &timeout) { + return format_cmd("PEXPIRE %b %lld", key.data(), key.size(), timeout.count()); +} + +inline FormattedCommand pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return format_cmd("PEXPIREAT %b %lld", key.data(), key.size(), tp.time_since_epoch().count()); +} + +inline FormattedCommand pttl(const StringView &key) { + return format_cmd("PTTL %b", key.data(), key.size()); +} + +inline FormattedCommand rename(const StringView &key, const StringView &newkey) { + return format_cmd("RENAME %b %b", key.data(), key.size(), newkey.data(), newkey.size()); +} + +inline FormattedCommand renamenx(const StringView &key, const StringView &newkey) { + return format_cmd("RENAMENX %b %b", key.data(), key.size(), newkey.data(), newkey.size()); +} + +inline FormattedCommand ttl(const StringView &key) { + return format_cmd("TTL %b", key.data(), key.size()); +} + +inline FormattedCommand unlink(const StringView &key) { + return format_cmd("UNLINK %b", key.data(), key.size()); +} + +template +FormattedCommand unlink_range(Input first, Input last) { + CmdArgs args; + args << "UNLINK" << std::make_pair(first, last); + + return format_cmd(args); +} + +// STRING commands. + +inline FormattedCommand get(const StringView &key) { + return format_cmd("GET %b", key.data(), key.size()); +} + +inline FormattedCommand incr(const StringView &key) { + return format_cmd("INCR %b", key.data(), key.size()); +} + +inline FormattedCommand incrby(const StringView &key, long long increment) { + return format_cmd("INCRBY %b %lld", key.data(), key.size(), increment); +} + +inline FormattedCommand incrbyfloat(const StringView &key, double increment) { + return format_cmd("INCRBYFLOAT %b %f", key.data(), key.size(), increment); +} + +template +FormattedCommand mget(Input first, Input last) { + CmdArgs args; + args << "MGET" << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand mset(Input first, Input last) { + CmdArgs args; + args << "MSET" << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand msetnx(Input first, Input last) { + CmdArgs args; + args << "MSETNX" << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + UpdateType type) { + CmdArgs args; + args << "SET" << key << val; + + if (ttl > std::chrono::milliseconds(0)) { + args << "PX" << ttl.count(); + } + + cmd::detail::set_update_type(args, type); + + return format_cmd(args); +} + +inline FormattedCommand strlen(const StringView &key) { + return format_cmd("STRLEN %b", key.data(), key.size()); +} + +inline FormattedCommand blpop(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BLPOP %b %lld", key.data(), key.size(), timeout.count()); +} + +template +FormattedCommand blpop_range(Input first, Input last, const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BLPOP" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand brpop(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BRPOP %b %lld", key.data(), key.size(), timeout.count()); +} + + +template +FormattedCommand brpop_range(Input first, Input last, const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BRPOP" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return format_cmd("BRPOPLPUSH %b %b %lld", + source.data(), source.size(), + destination.data(), destination.size(), + timeout.count()); +} + +inline FormattedCommand llen(const StringView &key) { + return format_cmd("LLEN %b", key.data(), key.size()); +} + +inline FormattedCommand lpop(const StringView &key) { + return format_cmd("LPOP %b", key.data(), key.size()); +} + +inline FormattedCommand lpush(const StringView &key, const StringView &val) { + return format_cmd("LPUSH %b %b", key.data(), key.size(), val.data(), val.size()); +} + +template +FormattedCommand lpush_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "LPUSH" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand lrange(const StringView &key, long long start, long long stop) { + return format_cmd("LRANGE %b %lld %lld", key.data(), key.size(), start, stop); +} + +inline FormattedCommand lrem(const StringView &key, long long count, const StringView &val) { + return format_cmd("LREM %b %lld %b", key.data(), key.size(), count, val.data(), val.size()); +} + +inline FormattedCommand ltrim(const StringView &key, long long start, long long stop) { + return format_cmd("LTRIM %b %lld %lld", key.data(), key.size(), start, stop); +} + +inline FormattedCommand rpop(const StringView &key) { + return format_cmd("RPOP %b", key.data(), key.size()); +} + +inline FormattedCommand rpoplpush(const StringView &source, const StringView &destination) { + return format_cmd("RPOPLPUSH %b %b", + source.data(), source.size(), + destination.data(), destination.size()); +} + +inline FormattedCommand rpush(const StringView &key, const StringView &val) { + return format_cmd("RPUSH %b %b", key.data(), key.size(), val.data(), val.size()); +} + +template +FormattedCommand rpush_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "RPUSH" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +// HASH commands. + +inline FormattedCommand hdel(const StringView &key, const StringView &field) { + return format_cmd("HDEL %b %b", key.data(), key.size(), field.data(), field.size()); +} + +template +FormattedCommand hdel_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HDEL" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand hexists(const StringView &key, const StringView &field) { + return format_cmd("HEXISTS %b %b", key.data(), key.size(), field.data(), field.size()); +} + +inline FormattedCommand hget(const StringView &key, const StringView &field) { + return format_cmd("HGET %b %b", key.data(), key.size(), field.data(), field.size()); +} + +inline FormattedCommand hgetall(const StringView &key) { + return format_cmd("HGETALL %b", key.data(), key.size()); +} + +inline FormattedCommand hincrby(const StringView &key, + const StringView &field, + long long increment) { + return format_cmd("HINCRBY %b %b %lld", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline FormattedCommand hincrbyfloat(const StringView &key, + const StringView &field, + double increment) { + return format_cmd("HINCRBYFLOAT %b %b %f", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline FormattedCommand hkeys(const StringView &key) { + return format_cmd("HKEYS %b", key.data(), key.size()); +} + +inline FormattedCommand hlen(const StringView &key) { + return format_cmd("HLEN %b", key.data(), key.size()); +} + +template +FormattedCommand hmget(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HMGET" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand hmset(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HMSET" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand hset(const StringView &key, + const StringView &field, + const StringView &val) { + return format_cmd("HSET %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +template +auto hset_range(const StringView &key, + Input first, + Input last) + -> typename std::enable_if::value, + FormattedCommand>::type { + assert(first != last); + + CmdArgs args; + args << "HSET" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand hvals(const StringView &key) { + return format_cmd("HVALS %b", key.data(), key.size()); +} + +// SET commands. + +inline FormattedCommand sadd(const StringView &key, const StringView &member) { + return format_cmd("SADD %b %b", key.data(), key.size(), member.data(), member.size()); +} + +template +FormattedCommand sadd_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SADD" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand scard(const StringView &key) { + return format_cmd("SCARD %b", key.data(), key.size()); +} + +inline FormattedCommand sismember(const StringView &key, const StringView &member) { + return format_cmd("SISMEMBER %b %b", key.data(), key.size(), member.data(), member.size()); +} + +inline FormattedCommand smembers(const StringView &key) { + return format_cmd("SMEMBERS %b", key.data(), key.size()); +} + +inline FormattedCommand spop(const StringView &key) { + return format_cmd("SPOP %b", key.data(), key.size()); +} + +inline FormattedCommand spop(const StringView &key, long long count) { + return format_cmd("SPOP %b %lld", key.data(), key.size(), count); +} + +inline FormattedCommand srem(const StringView &key, const StringView &member) { + return format_cmd("SREM %b %b", key.data(), key.size(), member.data(), member.size()); +} + +template +FormattedCommand srem_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SREM" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +// SORTED SET commands. + +inline FormattedCommand bzpopmax(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BZPOPMAX %b %lld", key.data(), key.size(), timeout.count()); +} + +template +FormattedCommand bzpopmax_range(Input first, + Input last, + const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMAX" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand bzpopmin(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BZPOPMIN %b %lld", key.data(), key.size(), timeout.count()); +} + +template +FormattedCommand bzpopmin_range(Input first, Input last, const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMIN" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type, + bool changed) { + CmdArgs args; + args << "ZADD" << key; + + cmd::detail::set_update_type(args, type); + + if (changed) { + args << "CH"; + } + + args << score << member; + + return format_cmd(args); +} + +template +FormattedCommand zadd_range(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + CmdArgs args; + args << "ZADD" << key; + + cmd::detail::set_update_type(args, type); + + if (changed) { + args << "CH"; + } + + while (first != last) { + // Swap the pair to pair. + args << first->second << first->first; + ++first; + } + + return format_cmd(args); +} + +inline FormattedCommand zcard(const StringView &key) { + return format_cmd("ZCARD %b", key.data(), key.size()); +} + +template +FormattedCommand zcount(const StringView &key, const Interval &interval) { + return format_cmd("ZCOUNT %b %s %s", + key.data(), key.size(), + interval.min().c_str(), + interval.max().c_str()); +} + +inline FormattedCommand zincrby(const StringView &key, + double increment, + const StringView &member) { + return format_cmd("ZINCRBY %b %f %b", + key.data(), key.size(), + increment, + member.data(), member.size()); +} + +template +FormattedCommand zlexcount(const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZLEXCOUNT %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline FormattedCommand zpopmax(const StringView &key) { + return format_cmd("ZPOPMAX %b", key.data(), key.size()); +} + +inline FormattedCommand zpopmax(const StringView &key, long long count) { + return format_cmd("ZPOPMAX %b %lld", key.data(), key.size(), count); +} + +inline FormattedCommand zpopmin(const StringView &key) { + return format_cmd("ZPOPMIN %b", key.data(), key.size()); +} + +inline FormattedCommand zpopmin_count(const StringView &key, long long count) { + return format_cmd("ZPOPMIN %b %lld", key.data(), key.size(), count); +} + +inline FormattedCommand zrange(const StringView &key, long long start, long long stop) { + return format_cmd("ZRANGE %b %lld %lld", key.data(), key.size(), start, stop); +} + +template +FormattedCommand zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); +} + +template +FormattedCommand zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); +} + +inline FormattedCommand zrank(const StringView &key, const StringView &member) { + return format_cmd("ZRANK %b %b", key.data(), key.size(), member.data(), member.size()); +} + +inline FormattedCommand zrem(const StringView &key, const StringView &member) { + return format_cmd("ZREM %b %b", key.data(), key.size(), member.data(), member.size()); +} + +template +FormattedCommand zrem_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "ZREM" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand zremrangebylex(const StringView &key, const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZREMRANGEBYLEX %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline FormattedCommand zremrangebyrank(const StringView &key, long long start, long long stop) { + return format_cmd("ZREMRANGEBYRANK %b %lld %lld", key.data(), key.size(), start, stop); +} + +template +FormattedCommand zremrangebyscore(const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZREMRANGEBYSCORE %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +template +FormattedCommand zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZREVRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); +} + +inline FormattedCommand zrevrank(const StringView &key, const StringView &member) { + return format_cmd("ZREVRANK %b %b", key.data(), key.size(), member.data(), member.size()); +} + +inline FormattedCommand zscore(const StringView &key, const StringView &member) { + return format_cmd("ZSCORE %b %b", key.data(), key.size(), member.data(), member.size()); +} + +// SCRIPTING commands. +template +FormattedCommand eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs args; + auto keys_num = std::distance(keys_first, keys_last); + + args << "EVAL" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + return format_cmd(args); +} + +template +FormattedCommand evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs args; + auto keys_num = std::distance(keys_first, keys_last); + + args << "EVALSHA" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + return format_cmd(args); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CMD_FORMATTER_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/command.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/command.cpp new file mode 100644 index 000000000..bb1afb2a9 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/command.cpp @@ -0,0 +1,376 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "command.h" +#include + +namespace sw { + +namespace redis { + +namespace cmd { + +// KEY commands. + +void restore(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + bool replace) { + CmdArgs args; + args << "RESTORE" << key << ttl << val; + + if (replace) { + args << "REPLACE"; + } + + connection.send(args); +} + +// STRING commands. + +void bitop(Connection &connection, + BitOp op, + const StringView &destination, + const StringView &key) { + CmdArgs args; + + detail::set_bitop(args, op); + + args << destination << key; + + connection.send(args); +} + +void set(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + UpdateType type) { + CmdArgs args; + args << "SET" << key << val; + + if (ttl > 0) { + args << "PX" << ttl; + } + + detail::set_update_type(args, type); + + connection.send(args); +} + +// LIST commands. + +void linsert(Connection &connection, + const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val) { + std::string pos; + switch (position) { + case InsertPosition::BEFORE: + pos = "BEFORE"; + break; + + case InsertPosition::AFTER: + pos = "AFTER"; + break; + + default: + assert(false); + } + + connection.send("LINSERT %b %s %b %b", + key.data(), key.size(), + pos.c_str(), + pivot.data(), pivot.size(), + val.data(), val.size()); +} + +// GEO commands. + +void geodist(Connection &connection, + const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit) { + CmdArgs args; + args << "GEODIST" << key << member1 << member2; + + detail::set_geo_unit(args, unit); + + connection.send(args); +} + +void georadius_store(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + CmdArgs args; + args << "GEORADIUS" << key << loc.first << loc.second; + + detail::set_georadius_store_parameters(args, + radius, + unit, + destination, + store_dist, + count); + + connection.send(args); +} + +void georadius(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + CmdArgs args; + args << "GEORADIUS" << key << loc.first << loc.second; + + detail::set_georadius_parameters(args, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + + connection.send(args); +} + +void georadiusbymember(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + CmdArgs args; + args << "GEORADIUSBYMEMBER" << key << member; + + detail::set_georadius_parameters(args, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + + connection.send(args); +} + +void georadiusbymember_store(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + CmdArgs args; + args << "GEORADIUSBYMEMBER" << key << member; + + detail::set_georadius_store_parameters(args, + radius, + unit, + destination, + store_dist, + count); + + connection.send(args); +} + +// Stream commands. + +void xtrim(Connection &connection, const StringView &key, long long count, bool approx) { + CmdArgs args; + args << "XTRIM" << key << "MAXLEN"; + + if (approx) { + args << "~"; + } + + args << count; + + connection.send(args); +} + +namespace detail { + +void set_bitop(CmdArgs &args, BitOp op) { + args << "BITOP"; + + switch (op) { + case BitOp::AND: + args << "AND"; + break; + + case BitOp::OR: + args << "OR"; + break; + + case BitOp::XOR: + args << "XOR"; + break; + + case BitOp::NOT: + args << "NOT"; + break; + + default: + throw Error("Unknown bit operations"); + } +} + +void set_update_type(CmdArgs &args, UpdateType type) { + switch (type) { + case UpdateType::EXIST: + args << "XX"; + break; + + case UpdateType::NOT_EXIST: + args << "NX"; + break; + + case UpdateType::ALWAYS: + // Do nothing. + break; + + default: + throw Error("Unknown update type"); + } +} + +void set_aggregation_type(CmdArgs &args, Aggregation aggr) { + args << "AGGREGATE"; + + switch (aggr) { + case Aggregation::SUM: + args << "SUM"; + break; + + case Aggregation::MIN: + args << "MIN"; + break; + + case Aggregation::MAX: + args << "MAX"; + break; + + default: + throw Error("Unknown aggregation type"); + } +} + +void set_geo_unit(CmdArgs &args, GeoUnit unit) { + switch (unit) { + case GeoUnit::M: + args << "m"; + break; + + case GeoUnit::KM: + args << "km"; + break; + + case GeoUnit::MI: + args << "mi"; + break; + + case GeoUnit::FT: + args << "ft"; + break; + + default: + throw Error("Unknown geo unit type"); + break; + } +} + +void set_georadius_store_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + args << radius; + + detail::set_geo_unit(args, unit); + + args << "COUNT" << count; + + if (store_dist) { + args << "STOREDIST"; + } else { + args << "STORE"; + } + + args << destination; +} + +void set_georadius_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + args << radius; + + detail::set_geo_unit(args, unit); + + if (with_coord) { + args << "WITHCOORD"; + } + + if (with_dist) { + args << "WITHDIST"; + } + + if (with_hash) { + args << "WITHHASH"; + } + + args << "COUNT" << count; + + if (asc) { + args << "ASC"; + } else { + args << "DESC"; + } +} + +} + +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/command.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/command.h new file mode 100644 index 000000000..81857dd9b --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/command.h @@ -0,0 +1,2271 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_H + +#include +#include +#include +#include +#include "connection.h" +#include "command_options.h" +#include "command_args.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace cmd { + +// CONNECTION command. +inline void auth(Connection &connection, const StringView &password) { + connection.send("AUTH %b", password.data(), password.size()); +} + +inline void auth(Connection &connection, const StringView &user, const StringView &password) { + connection.send("AUTH %b %b", + user.data(), user.size(), + password.data(), password.size()); +} + +inline void echo(Connection &connection, const StringView &msg) { + connection.send("ECHO %b", msg.data(), msg.size()); +} + +inline void ping(Connection &connection) { + connection.send("PING"); +} + +inline void quit(Connection &connection) { + connection.send("QUIT"); +} + +inline void ping(Connection &connection, const StringView &msg) { + // If *msg* is empty, Redis returns am empty reply of REDIS_REPLY_STRING type. + connection.send("PING %b", msg.data(), msg.size()); +} + +inline void select(Connection &connection, long long idx) { + connection.send("SELECT %lld", idx); +} + +inline void swapdb(Connection &connection, long long idx1, long long idx2) { + connection.send("SWAPDB %lld %lld", idx1, idx2); +} + +// SERVER commands. + +inline void bgrewriteaof(Connection &connection) { + connection.send("BGREWRITEAOF"); +} + +inline void bgsave(Connection &connection) { + connection.send("BGSAVE"); +} + +inline void dbsize(Connection &connection) { + connection.send("DBSIZE"); +} + +inline void flushall(Connection &connection, bool async) { + if (async) { + connection.send("FLUSHALL ASYNC"); + } else { + connection.send("FLUSHALL"); + } +} + +inline void flushdb(Connection &connection, bool async) { + if (async) { + connection.send("FLUSHDB ASYNC"); + } else { + connection.send("FLUSHDB"); + } +} + +inline void info(Connection &connection) { + connection.send("INFO"); +} + +inline void info(Connection &connection, const StringView §ion) { + connection.send("INFO %b", section.data(), section.size()); +} + +inline void lastsave(Connection &connection) { + connection.send("LASTSAVE"); +} + +inline void save(Connection &connection) { + connection.send("SAVE"); +} + +// KEY commands. + +inline void del(Connection &connection, const StringView &key) { + connection.send("DEL %b", key.data(), key.size()); +} + +template +inline void del_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "DEL" << std::make_pair(first, last); + + connection.send(args); +} + +inline void dump(Connection &connection, const StringView &key) { + connection.send("DUMP %b", key.data(), key.size()); +} + +inline void exists(Connection &connection, const StringView &key) { + connection.send("EXISTS %b", key.data(), key.size()); +} + +template +inline void exists_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "EXISTS" << std::make_pair(first, last); + + connection.send(args); +} + +inline void expire(Connection &connection, + const StringView &key, + long long timeout) { + connection.send("EXPIRE %b %lld", + key.data(), key.size(), + timeout); +} + +inline void expireat(Connection &connection, + const StringView &key, + long long timestamp) { + connection.send("EXPIREAT %b %lld", + key.data(), key.size(), + timestamp); +} + +inline void keys(Connection &connection, const StringView &pattern) { + connection.send("KEYS %b", pattern.data(), pattern.size()); +} + +inline void move(Connection &connection, const StringView &key, long long db) { + connection.send("MOVE %b %lld", + key.data(), key.size(), + db); +} + +inline void persist(Connection &connection, const StringView &key) { + connection.send("PERSIST %b", key.data(), key.size()); +} + +inline void pexpire(Connection &connection, + const StringView &key, + long long timeout) { + connection.send("PEXPIRE %b %lld", + key.data(), key.size(), + timeout); +} + +inline void pexpireat(Connection &connection, + const StringView &key, + long long timestamp) { + connection.send("PEXPIREAT %b %lld", + key.data(), key.size(), + timestamp); +} + +inline void pttl(Connection &connection, const StringView &key) { + connection.send("PTTL %b", key.data(), key.size()); +} + +inline void randomkey(Connection &connection) { + connection.send("RANDOMKEY"); +} + +inline void rename(Connection &connection, + const StringView &key, + const StringView &newkey) { + connection.send("RENAME %b %b", + key.data(), key.size(), + newkey.data(), newkey.size()); +} + +inline void renamenx(Connection &connection, + const StringView &key, + const StringView &newkey) { + connection.send("RENAMENX %b %b", + key.data(), key.size(), + newkey.data(), newkey.size()); +} + +void restore(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + bool replace); + +inline void scan(Connection &connection, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("SCAN %lld MATCH %b COUNT %lld", + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void touch(Connection &connection, const StringView &key) { + connection.send("TOUCH %b", key.data(), key.size()); +} + +template +inline void touch_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "TOUCH" << std::make_pair(first, last); + + connection.send(args); +} + +inline void ttl(Connection &connection, const StringView &key) { + connection.send("TTL %b", key.data(), key.size()); +} + +inline void type(Connection &connection, const StringView &key) { + connection.send("TYPE %b", key.data(), key.size()); +} + +inline void unlink(Connection &connection, const StringView &key) { + connection.send("UNLINK %b", key.data(), key.size()); +} + +template +inline void unlink_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "UNLINK" << std::make_pair(first, last); + + connection.send(args); +} + +inline void wait(Connection &connection, long long numslave, long long timeout) { + connection.send("WAIT %lld %lld", numslave, timeout); +} + +// STRING commands. + +inline void append(Connection &connection, const StringView &key, const StringView &str) { + connection.send("APPEND %b %b", + key.data(), key.size(), + str.data(), str.size()); +} + +inline void bitcount(Connection &connection, + const StringView &key, + long long start, + long long end) { + connection.send("BITCOUNT %b %lld %lld", + key.data(), key.size(), + start, end); +} + +void bitop(Connection &connection, + BitOp op, + const StringView &destination, + const StringView &key); + +template +void bitop_range(Connection &connection, + BitOp op, + const StringView &destination, + Input first, + Input last); + +inline void bitpos(Connection &connection, + const StringView &key, + long long bit, + long long start, + long long end) { + connection.send("BITPOS %b %lld %lld %lld", + key.data(), key.size(), + bit, + start, + end); +} + +inline void decr(Connection &connection, const StringView &key) { + connection.send("DECR %b", key.data(), key.size()); +} + +inline void decrby(Connection &connection, const StringView &key, long long decrement) { + connection.send("DECRBY %b %lld", + key.data(), key.size(), + decrement); +} + +inline void get(Connection &connection, const StringView &key) { + connection.send("GET %b", + key.data(), key.size()); +} + +inline void getbit(Connection &connection, const StringView &key, long long offset) { + connection.send("GETBIT %b %lld", + key.data(), key.size(), + offset); +} + +inline void getrange(Connection &connection, + const StringView &key, + long long start, + long long end) { + connection.send("GETRANGE %b %lld %lld", + key.data(), key.size(), + start, + end); +} + +inline void getset(Connection &connection, + const StringView &key, + const StringView &val) { + connection.send("GETSET %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void incr(Connection &connection, const StringView &key) { + connection.send("INCR %b", key.data(), key.size()); +} + +inline void incrby(Connection &connection, const StringView &key, long long increment) { + connection.send("INCRBY %b %lld", + key.data(), key.size(), + increment); +} + +inline void incrbyfloat(Connection &connection, const StringView &key, double increment) { + connection.send("INCRBYFLOAT %b %f", + key.data(), key.size(), + increment); +} + +template +inline void mget(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MGET" << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void mset(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MSET" << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void msetnx(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MSETNX" << std::make_pair(first, last); + + connection.send(args); +} + +inline void psetex(Connection &connection, + const StringView &key, + long long ttl, + const StringView &val) { + connection.send("PSETEX %b %lld %b", + key.data(), key.size(), + ttl, + val.data(), val.size()); +} + +void set(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + UpdateType type); + +inline void setex(Connection &connection, + const StringView &key, + long long ttl, + const StringView &val) { + connection.send("SETEX %b %lld %b", + key.data(), key.size(), + ttl, + val.data(), val.size()); +} + +inline void setnx(Connection &connection, + const StringView &key, + const StringView &val) { + connection.send("SETNX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void setrange(Connection &connection, + const StringView &key, + long long offset, + const StringView &val) { + connection.send("SETRANGE %b %lld %b", + key.data(), key.size(), + offset, + val.data(), val.size()); +} + +inline void strlen(Connection &connection, const StringView &key) { + connection.send("STRLEN %b", key.data(), key.size()); +} + +// LIST commands. + +inline void blpop(Connection &connection, const StringView &key, long long timeout) { + connection.send("BLPOP %b %lld", + key.data(), key.size(), + timeout); +} + +template +inline void blpop_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BLPOP" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void brpop(Connection &connection, const StringView &key, long long timeout) { + connection.send("BRPOP %b %lld", + key.data(), key.size(), + timeout); +} + +template +inline void brpop_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BRPOP" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void brpoplpush(Connection &connection, + const StringView &source, + const StringView &destination, + long long timeout) { + connection.send("BRPOPLPUSH %b %b %lld", + source.data(), source.size(), + destination.data(), destination.size(), + timeout); +} + +inline void lindex(Connection &connection, const StringView &key, long long index) { + connection.send("LINDEX %b %lld", + key.data(), key.size(), + index); +} + +void linsert(Connection &connection, + const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + +inline void llen(Connection &connection, + const StringView &key) { + connection.send("LLEN %b", key.data(), key.size()); +} + +inline void lpop(Connection &connection, const StringView &key) { + connection.send("LPOP %b", + key.data(), key.size()); +} + +inline void lpush(Connection &connection, const StringView &key, const StringView &val) { + connection.send("LPUSH %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +template +inline void lpush_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "LPUSH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void lpushx(Connection &connection, const StringView &key, const StringView &val) { + connection.send("LPUSHX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void lrange(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("LRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +inline void lrem(Connection &connection, + const StringView &key, + long long count, + const StringView &val) { + connection.send("LREM %b %lld %b", + key.data(), key.size(), + count, + val.data(), val.size()); +} + +inline void lset(Connection &connection, + const StringView &key, + long long index, + const StringView &val) { + connection.send("LSET %b %lld %b", + key.data(), key.size(), + index, + val.data(), val.size()); +} + +inline void ltrim(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("LTRIM %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +inline void rpop(Connection &connection, const StringView &key) { + connection.send("RPOP %b", key.data(), key.size()); +} + +inline void rpoplpush(Connection &connection, + const StringView &source, + const StringView &destination) { + connection.send("RPOPLPUSH %b %b", + source.data(), source.size(), + destination.data(), destination.size()); +} + +inline void rpush(Connection &connection, const StringView &key, const StringView &val) { + connection.send("RPUSH %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +template +inline void rpush_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "RPUSH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void rpushx(Connection &connection, const StringView &key, const StringView &val) { + connection.send("RPUSHX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +// HASH commands. + +inline void hdel(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HDEL %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +template +inline void hdel_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HDEL" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hexists(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HEXISTS %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hget(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HGET %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hgetall(Connection &connection, const StringView &key) { + connection.send("HGETALL %b", key.data(), key.size()); +} + +inline void hincrby(Connection &connection, + const StringView &key, + const StringView &field, + long long increment) { + connection.send("HINCRBY %b %b %lld", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline void hincrbyfloat(Connection &connection, + const StringView &key, + const StringView &field, + double increment) { + connection.send("HINCRBYFLOAT %b %b %f", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline void hkeys(Connection &connection, const StringView &key) { + connection.send("HKEYS %b", key.data(), key.size()); +} + +inline void hlen(Connection &connection, const StringView &key) { + connection.send("HLEN %b", key.data(), key.size()); +} + +template +inline void hmget(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HMGET" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void hmset(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HMSET" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("HSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void hset(Connection &connection, + const StringView &key, + const StringView &field, + const StringView &val) { + connection.send("HSET %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +template +void hset_range(Connection &connection, const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HSET" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hsetnx(Connection &connection, + const StringView &key, + const StringView &field, + const StringView &val) { + connection.send("HSETNX %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +inline void hstrlen(Connection &connection, + const StringView &key, + const StringView &field) { + connection.send("HSTRLEN %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hvals(Connection &connection, const StringView &key) { + connection.send("HVALS %b", key.data(), key.size()); +} + +// SET commands + +inline void sadd(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SADD %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void sadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SADD" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void scard(Connection &connection, const StringView &key) { + connection.send("SCARD %b", key.data(), key.size()); +} + +template +inline void sdiff(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SDIFF" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sdiffstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SDIFFSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sdiffstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SDIFFSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void sinter(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SINTER" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sinterstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SINTERSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SINTERSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +inline void sismember(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SISMEMBER %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void smembers(Connection &connection, const StringView &key) { + connection.send("SMEMBERS %b", key.data(), key.size()); +} + +inline void smove(Connection &connection, + const StringView &source, + const StringView &destination, + const StringView &member) { + connection.send("SMOVE %b %b %b", + source.data(), source.size(), + destination.data(), destination.size(), + member.data(), member.size()); +} + +inline void spop(Connection &connection, const StringView &key) { + connection.send("SPOP %b", key.data(), key.size()); +} + +inline void spop_range(Connection &connection, const StringView &key, long long count) { + connection.send("SPOP %b %lld", + key.data(), key.size(), + count); +} + +inline void srandmember(Connection &connection, const StringView &key) { + connection.send("SRANDMEMBER %b", key.data(), key.size()); +} + +inline void srandmember_range(Connection &connection, + const StringView &key, + long long count) { + connection.send("SRANDMEMBER %b %lld", + key.data(), key.size(), + count); +} + +inline void srem(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SREM %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void srem_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SREM" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void sscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("SSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +template +inline void sunion(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SUNION" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sunionstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SUNIONSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SUNIONSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +// Sorted Set commands. + +inline void bzpopmax(Connection &connection, const StringView &key, long long timeout) { + connection.send("BZPOPMAX %b %lld", key.data(), key.size(), timeout); +} + +template +void bzpopmax_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMAX" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void bzpopmin(Connection &connection, const StringView &key, long long timeout) { + connection.send("BZPOPMIN %b %lld", key.data(), key.size(), timeout); +} + +template +void bzpopmin_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMIN" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +template +void zadd_range(Connection &connection, + const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed); + +inline void zadd(Connection &connection, + const StringView &key, + const StringView &member, + double score, + UpdateType type, + bool changed) { + auto tmp = {std::make_pair(member, score)}; + + zadd_range(connection, key, tmp.begin(), tmp.end(), type, changed); +} + +inline void zcard(Connection &connection, const StringView &key) { + connection.send("ZCARD %b", key.data(), key.size()); +} + +template +inline void zcount(Connection &connection, + const StringView &key, + const Interval &interval) { + connection.send("ZCOUNT %b %s %s", + key.data(), key.size(), + interval.min().c_str(), + interval.max().c_str()); +} + +inline void zincrby(Connection &connection, + const StringView &key, + double increment, + const StringView &member) { + connection.send("ZINCRBY %b %f %b", + key.data(), key.size(), + increment, + member.data(), member.size()); +} + +inline void zinterstore(Connection &connection, + const StringView &destination, + const StringView &key, + double weight) { + connection.send("ZINTERSTORE %b 1 %b WEIGHTS %f", + destination.data(), destination.size(), + key.data(), key.size(), + weight); +} + +template +void zinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr); + +template +inline void zlexcount(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZLEXCOUNT %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zpopmax(Connection &connection, const StringView &key, long long count) { + connection.send("ZPOPMAX %b %lld", + key.data(), key.size(), + count); +} + +inline void zpopmin(Connection &connection, const StringView &key, long long count) { + connection.send("ZPOPMIN %b %lld", + key.data(), key.size(), + count); +} + +inline void zrange(Connection &connection, + const StringView &key, + long long start, + long long stop, + bool with_scores) { + if (with_scores) { + connection.send("ZRANGE %b %lld %lld WITHSCORES", + key.data(), key.size(), + start, + stop); + } else { + connection.send("ZRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); + } +} + +template +inline void zrangebylex(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); +} + +template +void zrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + if (with_scores) { + connection.send("ZRANGEBYSCORE %b %b %b WITHSCORES LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); + } else { + connection.send("ZRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); + } +} + +inline void zrank(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZRANK %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zrem(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZREM %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void zrem_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "ZREM" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void zremrangebylex(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREMRANGEBYLEX %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zremrangebyrank(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("zremrangebyrank %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +template +inline void zremrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREMRANGEBYSCORE %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zrevrange(Connection &connection, + const StringView &key, + long long start, + long long stop, + bool with_scores) { + if (with_scores) { + connection.send("ZREVRANGE %b %lld %lld WITHSCORES", + key.data(), key.size(), + start, + stop); + } else { + connection.send("ZREVRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); + } +} + +template +inline void zrevrangebylex(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREVRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); +} + +template +void zrevrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + if (with_scores) { + connection.send("ZREVRANGEBYSCORE %b %b %b WITHSCORES LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); + } else { + connection.send("ZREVRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); + } +} + +inline void zrevrank(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZREVRANK %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("ZSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void zscore(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZSCORE %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zunionstore(Connection &connection, + const StringView &destination, + const StringView &key, + double weight) { + connection.send("ZUNIONSTORE %b 1 %b WEIGHTS %f", + destination.data(), destination.size(), + key.data(), key.size(), + weight); +} + +template +void zunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr); + +// HYPERLOGLOG commands. + +inline void pfadd(Connection &connection, + const StringView &key, + const StringView &element) { + connection.send("PFADD %b %b", + key.data(), key.size(), + element.data(), element.size()); +} + +template +inline void pfadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFADD" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void pfcount(Connection &connection, const StringView &key) { + connection.send("PFCOUNT %b", key.data(), key.size()); +} + +template +inline void pfcount_range(Connection &connection, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFCOUNT" << std::make_pair(first, last); + + connection.send(args); +} + +inline void pfmerge(Connection &connection, const StringView &destination, const StringView &key) { + connection.send("PFMERGE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void pfmerge_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFMERGE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +// GEO commands. + +inline void geoadd(Connection &connection, + const StringView &key, + const std::tuple &member) { + const auto &mem = std::get<0>(member); + + connection.send("GEOADD %b %f %f %b", + key.data(), key.size(), + std::get<1>(member), + std::get<2>(member), + mem.data(), mem.size()); +} + +template +inline void geoadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOADD" << key; + + while (first != last) { + const auto &member = *first; + args << std::get<1>(member) << std::get<2>(member) << std::get<0>(member); + ++first; + } + + connection.send(args); +} + +void geodist(Connection &connection, + const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit); + +inline void geohash(Connection &connection, const StringView &key, const StringView &member) { + connection.send("GEOHASH %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void geohash_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOHASH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void geopos(Connection &connection, const StringView &key, const StringView &member) { + connection.send("GEOPOS %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void geopos_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOPOS" << key << std::make_pair(first, last); + + connection.send(args); +} + +void georadius(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +void georadius_store(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +void georadiusbymember(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +void georadiusbymember_store(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +// SCRIPTING commands. + +template +inline void eval(Connection &connection, + const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs cmd_args; + + auto keys_num = std::distance(keys_first, keys_last); + + cmd_args << "EVAL" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + connection.send(cmd_args); +} + +template +inline void evalsha(Connection &connection, + const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs cmd_args; + + auto keys_num = std::distance(keys_first, keys_last); + + cmd_args << "EVALSHA" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + connection.send(cmd_args); +} + +inline void script_exists(Connection &connection, const StringView &sha) { + connection.send("SCRIPT EXISTS %b", sha.data(), sha.size()); +} + +template +inline void script_exists_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SCRIPT" << "EXISTS" << std::make_pair(first, last); + + connection.send(args); +} + +inline void script_flush(Connection &connection) { + connection.send("SCRIPT FLUSH"); +} + +inline void script_kill(Connection &connection) { + connection.send("SCRIPT KILL"); +} + +inline void script_load(Connection &connection, const StringView &script) { + connection.send("SCRIPT LOAD %b", script.data(), script.size()); +} + +// PUBSUB commands. + +inline void psubscribe(Connection &connection, const StringView &pattern) { + connection.send("PSUBSCRIBE %b", pattern.data(), pattern.size()); +} + +template +inline void psubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("PSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "PSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void publish(Connection &connection, + const StringView &channel, + const StringView &message) { + connection.send("PUBLISH %b %b", + channel.data(), channel.size(), + message.data(), message.size()); +} + +inline void punsubscribe(Connection &connection) { + connection.send("PUNSUBSCRIBE"); +} + +inline void punsubscribe(Connection &connection, const StringView &pattern) { + connection.send("PUNSUBSCRIBE %b", pattern.data(), pattern.size()); +} + +template +inline void punsubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("PUNSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "PUNSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void subscribe(Connection &connection, const StringView &channel) { + connection.send("SUBSCRIBE %b", channel.data(), channel.size()); +} + +template +inline void subscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("SUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "SUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void unsubscribe(Connection &connection) { + connection.send("UNSUBSCRIBE"); +} + +inline void unsubscribe(Connection &connection, const StringView &channel) { + connection.send("UNSUBSCRIBE %b", channel.data(), channel.size()); +} + +template +inline void unsubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("UNSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "UNSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +// Transaction commands. + +inline void discard(Connection &connection) { + connection.send("DISCARD"); +} + +inline void exec(Connection &connection) { + connection.send("EXEC"); +} + +inline void multi(Connection &connection) { + connection.send("MULTI"); +} + +inline void unwatch(Connection &connection) { + connection.send("UNWATCH"); +} + +template +inline void unwatch_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("UNWATCH: no key specified"); + } + + CmdArgs args; + args << "UNWATCH" << std::make_pair(first, last); + + connection.send(args); +} + +inline void watch(Connection &connection, const StringView &key) { + connection.send("WATCH %b", key.data(), key.size()); +} + +template +inline void watch_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("WATCH: no key specified"); + } + + CmdArgs args; + args << "WATCH" << std::make_pair(first, last); + + connection.send(args); +} + +// Stream commands. + +inline void xack(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id) { + connection.send("XACK %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + id.data(), id.size()); +} + +template +void xack_range(Connection &connection, + const StringView &key, + const StringView &group, + Input first, + Input last) { + CmdArgs args; + args << "XACK" << key << group << std::make_pair(first, last); + + connection.send(args); +} + +template +void xadd_range(Connection &connection, + const StringView &key, + const StringView &id, + Input first, + Input last) { + CmdArgs args; + args << "XADD" << key << id << std::make_pair(first, last); + + connection.send(args); +} + +template +void xadd_maxlen_range(Connection &connection, + const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + CmdArgs args; + args << "XADD" << key << "MAXLEN"; + + if (approx) { + args << "~"; + } + + args << count << id << std::make_pair(first, last); + + connection.send(args); +} + +inline void xclaim(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer, + long long min_idle_time, + const StringView &id) { + connection.send("XCLAIM %b %b %b %lld %b", + key.data(), key.size(), + group.data(), group.size(), + consumer.data(), consumer.size(), + min_idle_time, + id.data(), id.size()); +} + +template +void xclaim_range(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer, + long long min_idle_time, + Input first, + Input last) { + CmdArgs args; + args << "XCLAIM" << key << group << consumer << min_idle_time << std::make_pair(first, last); + + connection.send(args); +} + +inline void xdel(Connection &connection, const StringView &key, const StringView &id) { + connection.send("XDEL %b %b", key.data(), key.size(), id.data(), id.size()); +} + +template +void xdel_range(Connection &connection, const StringView &key, Input first, Input last) { + CmdArgs args; + args << "XDEL" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void xgroup_create(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream) { + CmdArgs args; + args << "XGROUP" << "CREATE" << key << group << id; + + if (mkstream) { + args << "MKSTREAM"; + } + + connection.send(args); +} + +inline void xgroup_setid(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id) { + connection.send("XGROUP SETID %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + id.data(), id.size()); +} + +inline void xgroup_destroy(Connection &connection, + const StringView &key, + const StringView &group) { + connection.send("XGROUP DESTROY %b %b", + key.data(), key.size(), + group.data(), group.size()); +} + +inline void xgroup_delconsumer(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer) { + connection.send("XGROUP DELCONSUMER %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + consumer.data(), consumer.size()); +} + +inline void xlen(Connection &connection, const StringView &key) { + connection.send("XLEN %b", key.data(), key.size()); +} + +inline void xpending(Connection &connection, const StringView &key, const StringView &group) { + connection.send("XPENDING %b %b", + key.data(), key.size(), + group.data(), group.size()); +} + +inline void xpending_detail(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count) { + connection.send("XPENDING %b %b %b %b %lld", + key.data(), key.size(), + group.data(), group.size(), + start.data(), start.size(), + end.data(), end.size(), + count); +} + +inline void xpending_per_consumer(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer) { + connection.send("XPENDING %b %b %b %b %lld %b", + key.data(), key.size(), + group.data(), group.size(), + start.data(), start.size(), + end.data(), end.size(), + count, + consumer.data(), consumer.size()); +} + +inline void xrange(Connection &connection, + const StringView &key, + const StringView &start, + const StringView &end) { + connection.send("XRANGE %b %b %b", + key.data(), key.size(), + start.data(), start.size(), + end.data(), end.size()); +} + +inline void xrange_count(Connection &connection, + const StringView &key, + const StringView &start, + const StringView &end, + long long count) { + connection.send("XRANGE %b %b %b COUNT %lld", + key.data(), key.size(), + start.data(), start.size(), + end.data(), end.size(), + count); +} + +inline void xread(Connection &connection, + const StringView &key, + const StringView &id, + long long count) { + connection.send("XREAD COUNT %lld STREAMS %b %b", + count, + key.data(), key.size(), + id.data(), id.size()); +} + +template +void xread_range(Connection &connection, Input first, Input last, long long count) { + CmdArgs args; + args << "XREAD" << "COUNT" << count << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xread_block(Connection &connection, + const StringView &key, + const StringView &id, + long long timeout, + long long count) { + connection.send("XREAD COUNT %lld BLOCK %lld STREAMS %b %b", + count, + timeout, + key.data(), key.size(), + id.data(), id.size()); +} + +template +void xread_block_range(Connection &connection, + Input first, + Input last, + long long timeout, + long long count) { + CmdArgs args; + args << "XREAD" << "COUNT" << count << "BLOCK" << timeout << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xreadgroup(Connection &connection, + const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer << "COUNT" << count; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS" << key << id; + + connection.send(args); +} + +template +void xreadgroup_range(Connection &connection, + const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer << "COUNT" << count; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xreadgroup_block(Connection &connection, + const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long timeout, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer + << "COUNT" << count << "BLOCK" << timeout; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS" << key << id; + + connection.send(args); +} + +template +void xreadgroup_block_range(Connection &connection, + const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long timeout, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer + << "COUNT" << count << "BLOCK" << timeout; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xrevrange(Connection &connection, + const StringView &key, + const StringView &end, + const StringView &start) { + connection.send("XREVRANGE %b %b %b", + key.data(), key.size(), + end.data(), end.size(), + start.data(), start.size()); +} + +inline void xrevrange_count(Connection &connection, + const StringView &key, + const StringView &end, + const StringView &start, + long long count) { + connection.send("XREVRANGE %b %b %b COUNT %lld", + key.data(), key.size(), + end.data(), end.size(), + start.data(), start.size(), + count); +} + +void xtrim(Connection &connection, const StringView &key, long long count, bool approx); + +namespace detail { + +void set_bitop(CmdArgs &args, BitOp op); + +void set_update_type(CmdArgs &args, UpdateType type); + +void set_aggregation_type(CmdArgs &args, Aggregation type); + +template +void zinterstore(std::false_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZINTERSTORE" << destination << std::distance(first, last) + << std::make_pair(first, last); + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zinterstore(std::true_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZINTERSTORE" << destination << std::distance(first, last); + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + args << "WEIGHTS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zunionstore(std::false_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZUNIONSTORE" << destination << std::distance(first, last) + << std::make_pair(first, last); + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zunionstore(std::true_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZUNIONSTORE" << destination << std::distance(first, last); + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + args << "WEIGHTS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +void set_geo_unit(CmdArgs &args, GeoUnit unit); + +void set_georadius_store_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +void set_georadius_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +} + +} + +} + +} + +namespace sw { + +namespace redis { + +namespace cmd { + +template +void bitop_range(Connection &connection, + BitOp op, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + + detail::set_bitop(args, op); + + args << destination << std::make_pair(first, last); + + connection.send(args); +} + +template +void zadd_range(Connection &connection, + const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + assert(first != last); + + CmdArgs args; + + args << "ZADD" << key; + + detail::set_update_type(args, type); + + if (changed) { + args << "CH"; + } + + while (first != last) { + // Swap the pair to pair. + args << first->second << first->first; + ++first; + } + + connection.send(args); +} + +template +void zinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + assert(first != last); + + detail::zinterstore(typename IsKvPairIter::type(), + connection, + destination, + first, + last, + aggr); +} + +template +void zunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + assert(first != last); + + detail::zunionstore(typename IsKvPairIter::type(), + connection, + destination, + first, + last, + aggr); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/command_args.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/command_args.h new file mode 100644 index 000000000..0beb71e5c --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/command_args.h @@ -0,0 +1,180 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H + +#include +#include +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +class CmdArgs { +public: + template + CmdArgs& append(Arg &&arg); + + template + CmdArgs& append(Arg &&arg, Args &&...args); + + // All overloads of operator<< are for internal use only. + CmdArgs& operator<<(const StringView &arg); + + template ::type>::value, + int>::type = 0> + CmdArgs& operator<<(T &&arg); + + template + CmdArgs& operator<<(const std::pair &range); + + template + auto operator<<(const std::tuple &) -> + typename std::enable_if::type { + return *this; + } + + template + auto operator<<(const std::tuple &arg) -> + typename std::enable_if::type; + + const char** argv() { + return _argv.data(); + } + + const std::size_t* argv_len() { + return _argv_len.data(); + } + + std::size_t size() const { + return _argv.size(); + } + +private: + // Deep copy. + CmdArgs& _append(std::string arg); + + // Shallow copy. + CmdArgs& _append(const StringView &arg); + + // Shallow copy. + CmdArgs& _append(const char *arg); + + template ::type>::value, + int>::type = 0> + CmdArgs& _append(T &&arg) { + return operator<<(std::forward(arg)); + } + + template + CmdArgs& _append(std::true_type, const std::pair &range); + + template + CmdArgs& _append(std::false_type, const std::pair &range); + + std::vector _argv; + std::vector _argv_len; + + std::list _args; +}; + +template +inline CmdArgs& CmdArgs::append(Arg &&arg) { + return _append(std::forward(arg)); +} + +template +inline CmdArgs& CmdArgs::append(Arg &&arg, Args &&...args) { + _append(std::forward(arg)); + + return append(std::forward(args)...); +} + +inline CmdArgs& CmdArgs::operator<<(const StringView &arg) { + _argv.push_back(arg.data()); + _argv_len.push_back(arg.size()); + + return *this; +} + +template +inline CmdArgs& CmdArgs::operator<<(const std::pair &range) { + return _append(IsKvPair())>::type>(), range); +} + +template ::type>::value, + int>::type> +inline CmdArgs& CmdArgs::operator<<(T &&arg) { + return _append(std::to_string(std::forward(arg))); +} + +template +auto CmdArgs::operator<<(const std::tuple &arg) -> + typename std::enable_if::type { + operator<<(std::get(arg)); + + return operator<<(arg); +} + +inline CmdArgs& CmdArgs::_append(std::string arg) { + _args.push_back(std::move(arg)); + return operator<<(_args.back()); +} + +inline CmdArgs& CmdArgs::_append(const StringView &arg) { + return operator<<(arg); +} + +inline CmdArgs& CmdArgs::_append(const char *arg) { + return operator<<(arg); +} + +template +CmdArgs& CmdArgs::_append(std::false_type, const std::pair &range) { + auto first = range.first; + auto last = range.second; + while (first != last) { + *this << *first; + ++first; + } + + return *this; +} + +template +CmdArgs& CmdArgs::_append(std::true_type, const std::pair &range) { + auto first = range.first; + auto last = range.second; + while (first != last) { + *this << first->first << first->second; + ++first; + } + + return *this; +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/command_options.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/command_options.cpp new file mode 100644 index 000000000..0c9254e86 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/command_options.cpp @@ -0,0 +1,201 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "command_options.h" +#include "errors.h" + +namespace { + +const std::string NEGATIVE_INFINITY_NUMERIC = "-inf"; +const std::string POSITIVE_INFINITY_NUMERIC = "+inf"; + +const std::string NEGATIVE_INFINITY_STRING = "-"; +const std::string POSITIVE_INFINITY_STRING = "+"; + +std::string unbound(const std::string &bnd); + +std::string bound(const std::string &bnd); + +} + +namespace sw { + +namespace redis { + +const std::string& UnboundedInterval::min() const { + return NEGATIVE_INFINITY_NUMERIC; +} + +const std::string& UnboundedInterval::max() const { + return POSITIVE_INFINITY_NUMERIC; +} + +BoundedInterval::BoundedInterval(double min, double max, BoundType type) : + _min(std::to_string(min)), + _max(std::to_string(max)) { + switch (type) { + case BoundType::CLOSED: + // Do nothing + break; + + case BoundType::OPEN: + _min = unbound(_min); + _max = unbound(_max); + break; + + case BoundType::LEFT_OPEN: + _min = unbound(_min); + break; + + case BoundType::RIGHT_OPEN: + _max = unbound(_max); + break; + + default: + throw Error("Unknow BoundType"); + } +} + +LeftBoundedInterval::LeftBoundedInterval(double min, BoundType type) : + _min(std::to_string(min)) { + switch (type) { + case BoundType::OPEN: + _min = unbound(_min); + break; + + case BoundType::RIGHT_OPEN: + // Do nothing. + break; + + default: + throw Error("Bound type can only be OPEN or RIGHT_OPEN"); + } +} + +const std::string& LeftBoundedInterval::max() const { + return POSITIVE_INFINITY_NUMERIC; +} + +RightBoundedInterval::RightBoundedInterval(double max, BoundType type) : + _max(std::to_string(max)) { + switch (type) { + case BoundType::OPEN: + _max = unbound(_max); + break; + + case BoundType::LEFT_OPEN: + // Do nothing. + break; + + default: + throw Error("Bound type can only be OPEN or LEFT_OPEN"); + } +} + +const std::string& RightBoundedInterval::min() const { + return NEGATIVE_INFINITY_NUMERIC; +} + +const std::string& UnboundedInterval::min() const { + return NEGATIVE_INFINITY_STRING; +} + +const std::string& UnboundedInterval::max() const { + return POSITIVE_INFINITY_STRING; +} + +BoundedInterval::BoundedInterval(const std::string &min, + const std::string &max, + BoundType type) { + switch (type) { + case BoundType::CLOSED: + _min = bound(min); + _max = bound(max); + break; + + case BoundType::OPEN: + _min = unbound(min); + _max = unbound(max); + break; + + case BoundType::LEFT_OPEN: + _min = unbound(min); + _max = bound(max); + break; + + case BoundType::RIGHT_OPEN: + _min = bound(min); + _max = unbound(max); + break; + + default: + throw Error("Unknow BoundType"); + } +} + +LeftBoundedInterval::LeftBoundedInterval(const std::string &min, BoundType type) { + switch (type) { + case BoundType::OPEN: + _min = unbound(min); + break; + + case BoundType::RIGHT_OPEN: + _min = bound(min); + break; + + default: + throw Error("Bound type can only be OPEN or RIGHT_OPEN"); + } +} + +const std::string& LeftBoundedInterval::max() const { + return POSITIVE_INFINITY_STRING; +} + +RightBoundedInterval::RightBoundedInterval(const std::string &max, BoundType type) { + switch (type) { + case BoundType::OPEN: + _max = unbound(max); + break; + + case BoundType::LEFT_OPEN: + _max = bound(max); + break; + + default: + throw Error("Bound type can only be OPEN or LEFT_OPEN"); + } +} + +const std::string& RightBoundedInterval::min() const { + return NEGATIVE_INFINITY_STRING; +} + +} + +} + +namespace { + +std::string unbound(const std::string &bnd) { + return "(" + bnd; +} + +std::string bound(const std::string &bnd) { + return "[" + bnd; +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/command_options.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/command_options.h new file mode 100644 index 000000000..ca766c086 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/command_options.h @@ -0,0 +1,211 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H + +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +enum class UpdateType { + EXIST, + NOT_EXIST, + ALWAYS +}; + +enum class InsertPosition { + BEFORE, + AFTER +}; + +enum class BoundType { + CLOSED, + OPEN, + LEFT_OPEN, + RIGHT_OPEN +}; + +// (-inf, +inf) +template +class UnboundedInterval; + +// [min, max], (min, max), (min, max], [min, max) +template +class BoundedInterval; + +// [min, +inf), (min, +inf) +template +class LeftBoundedInterval; + +// (-inf, max], (-inf, max) +template +class RightBoundedInterval; + +template <> +class UnboundedInterval { +public: + const std::string& min() const; + + const std::string& max() const; +}; + +template <> +class BoundedInterval { +public: + BoundedInterval(double min, double max, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const { + return _max; + } + +private: + std::string _min; + std::string _max; +}; + +template <> +class LeftBoundedInterval { +public: + LeftBoundedInterval(double min, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const; + +private: + std::string _min; +}; + +template <> +class RightBoundedInterval { +public: + RightBoundedInterval(double max, BoundType type); + + const std::string& min() const; + + const std::string& max() const { + return _max; + } + +private: + std::string _max; +}; + +template <> +class UnboundedInterval { +public: + const std::string& min() const; + + const std::string& max() const; +}; + +template <> +class BoundedInterval { +public: + BoundedInterval(const std::string &min, const std::string &max, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const { + return _max; + } + +private: + std::string _min; + std::string _max; +}; + +template <> +class LeftBoundedInterval { +public: + LeftBoundedInterval(const std::string &min, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const; + +private: + std::string _min; +}; + +template <> +class RightBoundedInterval { +public: + RightBoundedInterval(const std::string &max, BoundType type); + + const std::string& min() const; + + const std::string& max() const { + return _max; + } + +private: + std::string _max; +}; + +struct LimitOptions { + long long offset = 0; + long long count = -1; +}; + +enum class Aggregation { + SUM, + MIN, + MAX +}; + +enum class BitOp { + AND, + OR, + XOR, + NOT +}; + +enum class GeoUnit { + M, + KM, + MI, + FT +}; + +template +struct WithCoord : TupleWithType, T> {}; + +template +struct WithDist : TupleWithType {}; + +template +struct WithHash : TupleWithType {}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection.cpp new file mode 100644 index 000000000..f0a808827 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection.cpp @@ -0,0 +1,494 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "connection.h" +#include +#include +#include +#include "reply.h" +#include "command.h" +#include "command_args.h" + +#ifdef _MSC_VER + +#include // for `timeval` with MSVC compiler + +#endif + +namespace sw { + +namespace redis { + +ConnectionOptions::ConnectionOptions(const std::string &uri) : + ConnectionOptions(_parse_uri(uri)) {} + +ConnectionOptions ConnectionOptions::_parse_uri(const std::string &uri) const { + std::string type; + std::string auth; + std::string path; + std::tie(type, auth, path) = _split_uri(uri); + + ConnectionOptions opts; + + _set_auth_opts(auth, opts); + + auto db = 0; + std::string parameter_string; + std::tie(path, db, parameter_string) = _split_path(path); + + _parse_parameters(parameter_string, opts); + + opts.db = db; + + if (type == "tcp") { + _set_tcp_opts(path, opts); + } else if (type == "unix") { + _set_unix_opts(path, opts); + } else { + throw Error("invalid URI: invalid type"); + } + + return opts; +} + +void ConnectionOptions::_parse_parameters(const std::string ¶meter_string, + ConnectionOptions &opts) const { + auto parameters = _split(parameter_string, "&"); + if (parameters.empty()) { + // No parameters + return; + } + + for (const auto ¶meter : parameters) { + auto kv_pair = _split(parameter, "="); + if (kv_pair.size() != 2) { + throw Error("invalid option: not a key-value pair: " + parameter); + } + + const auto &key = kv_pair[0]; + const auto &val = kv_pair[1]; + _set_option(key, val, opts); + } +} + +void ConnectionOptions::_set_option(const std::string &key, + const std::string &val, + ConnectionOptions &opts) const { + if (key == "keep_alive") { + opts.keep_alive = _parse_bool_option(val); + } else if (key == "connect_timeout") { + opts.connect_timeout = _parse_timeout_option(val); + } else if (key == "socket_timeout") { + opts.socket_timeout = _parse_timeout_option(val); + } else { + throw Error("unknown uri parameter"); + } +} + +bool ConnectionOptions::_parse_bool_option(const std::string &str) const { + if (str == "true") { + return true; + } else if (str == "false") { + return false; + } else { + throw Error("invalid uri parameter of bool type: " + str); + } +} + +std::chrono::milliseconds ConnectionOptions::_parse_timeout_option(const std::string &str) const { + std::size_t timeout = 0; + std::string unit; + try { + std::size_t pos = 0; + timeout = std::stoul(str, &pos); + unit = str.substr(pos); + } catch (const std::exception &e) { + throw Error("invalid uri parameter of timeout type: " + str); + } + + if (unit == "ms") { + return std::chrono::milliseconds(timeout); + } else if (unit == "s") { + return std::chrono::seconds(timeout); + } else if (unit == "m") { + return std::chrono::minutes(timeout); + } else { + throw Error("unknown timeout unit: " + unit); + } +} + +std::vector ConnectionOptions::_split(const std::string &str, + const std::string &delimiter) const { + if (str.empty()) { + return {}; + } + + std::vector fields; + + if (delimiter.empty()) { + std::transform(str.begin(), str.end(), std::back_inserter(fields), + [](char c) { return std::string(1, c); }); + return fields; + } + + std::string::size_type pos = 0; + std::string::size_type idx = 0; + while (true) { + pos = str.find(delimiter, idx); + if (pos == std::string::npos) { + fields.push_back(str.substr(idx)); + break; + } + + fields.push_back(str.substr(idx, pos - idx)); + idx = pos + delimiter.size(); + } + + return fields; +} + +auto ConnectionOptions::_split_uri(const std::string &uri) const + -> std::tuple { + auto pos = uri.find("://"); + if (pos == std::string::npos) { + throw Error("invalid URI: no scheme"); + } + + auto type = uri.substr(0, pos); + + auto start = pos + 3; + pos = uri.find("@", start); + if (pos == std::string::npos) { + // No auth info. + return std::make_tuple(type, std::string{}, uri.substr(start)); + } + + auto auth = uri.substr(start, pos - start); + + return std::make_tuple(type, auth, uri.substr(pos + 1)); +} + +auto ConnectionOptions::_split_path(const std::string &path) const + -> std::tuple { + auto parameter_pos = path.rfind("?"); + std::string parameter_string; + if (parameter_pos != std::string::npos) { + parameter_string = path.substr(parameter_pos + 1); + } + + auto pos = path.rfind("/"); + if (pos != std::string::npos) { + // Might specified a db number. + try { + auto db = std::stoi(path.substr(pos + 1)); + + return std::make_tuple(path.substr(0, pos), db, parameter_string); + } catch (const std::exception &) { + // Not a db number, and it might be a path to unix domain socket. + } + } + + // No db number specified, and use default one, i.e. 0. + return std::make_tuple(path.substr(0, parameter_pos), 0, parameter_string); +} + +void ConnectionOptions::_set_auth_opts(const std::string &auth, ConnectionOptions &opts) const { + if (auth.empty()) { + // No auth info. + return; + } + + auto pos = auth.find(":"); + if (pos == std::string::npos) { + // No user name. + opts.password = auth; + } else { + opts.user = auth.substr(0, pos); + opts.password = auth.substr(pos + 1); + } +} + +void ConnectionOptions::_set_tcp_opts(const std::string &path, ConnectionOptions &opts) const { + opts.type = ConnectionType::TCP; + + auto pos = path.find(":"); + if (pos != std::string::npos) { + // Port number specified. + try { + opts.port = std::stoi(path.substr(pos + 1)); + } catch (const std::exception &) { + throw Error("invalid URI: invalid port"); + } + } // else use default port, i.e. 6379. + + opts.host = path.substr(0, pos); +} + +void ConnectionOptions::_set_unix_opts(const std::string &path, ConnectionOptions &opts) const { + opts.type = ConnectionType::UNIX; + opts.path = path; +} + +class Connection::Connector { +public: + explicit Connector(const ConnectionOptions &opts); + + ContextUPtr connect() const; + +private: + ContextUPtr _connect() const; + + redisContext* _connect_tcp() const; + + redisContext* _connect_unix() const; + + void _set_socket_timeout(redisContext &ctx) const; + + void _enable_keep_alive(redisContext &ctx) const; + + timeval _to_timeval(const std::chrono::milliseconds &dur) const; + + const ConnectionOptions &_opts; +}; + +Connection::Connector::Connector(const ConnectionOptions &opts) : _opts(opts) {} + +Connection::ContextUPtr Connection::Connector::connect() const { + auto ctx = _connect(); + + assert(ctx); + + if (ctx->err != REDIS_OK) { + throw_error(*ctx, "Failed to connect to Redis"); + } + + _set_socket_timeout(*ctx); + + _enable_keep_alive(*ctx); + + return ctx; +} + +Connection::ContextUPtr Connection::Connector::_connect() const { + redisContext *context = nullptr; + switch (_opts.type) { + case ConnectionType::TCP: + context = _connect_tcp(); + break; + + case ConnectionType::UNIX: + context = _connect_unix(); + break; + + default: + // Never goes here. + throw Error("Unknown connection type"); + } + + if (context == nullptr) { + throw Error("Failed to allocate memory for connection."); + } + + return ContextUPtr(context); +} + +redisContext* Connection::Connector::_connect_tcp() const { + if (_opts.connect_timeout > std::chrono::milliseconds(0)) { + return redisConnectWithTimeout(_opts.host.c_str(), + _opts.port, + _to_timeval(_opts.connect_timeout)); + } else { + return redisConnect(_opts.host.c_str(), _opts.port); + } +} + +redisContext* Connection::Connector::_connect_unix() const { + if (_opts.connect_timeout > std::chrono::milliseconds(0)) { + return redisConnectUnixWithTimeout( + _opts.path.c_str(), + _to_timeval(_opts.connect_timeout)); + } else { + return redisConnectUnix(_opts.path.c_str()); + } +} + +void Connection::Connector::_set_socket_timeout(redisContext &ctx) const { + if (_opts.socket_timeout <= std::chrono::milliseconds(0)) { + return; + } + + if (redisSetTimeout(&ctx, _to_timeval(_opts.socket_timeout)) != REDIS_OK) { + throw_error(ctx, "Failed to set socket timeout"); + } +} + +void Connection::Connector::_enable_keep_alive(redisContext &ctx) const { + if (!_opts.keep_alive) { + return; + } + + if (redisEnableKeepAlive(&ctx) != REDIS_OK) { + throw_error(ctx, "Failed to enable keep alive option"); + } +} + +timeval Connection::Connector::_to_timeval(const std::chrono::milliseconds &dur) const { + auto sec = std::chrono::duration_cast(dur); + auto msec = std::chrono::duration_cast(dur - sec); + + timeval t; + t.tv_sec = sec.count(); + t.tv_usec = msec.count(); + return t; +} + +void swap(Connection &lhs, Connection &rhs) noexcept { + std::swap(lhs._ctx, rhs._ctx); + std::swap(lhs._create_time, rhs._create_time); + std::swap(lhs._opts, rhs._opts); +} + +Connection::Connection(const ConnectionOptions &opts) : + _ctx(Connector(opts).connect()), + _create_time(std::chrono::steady_clock::now()), + _last_active(std::chrono::steady_clock::now()), + _opts(opts) { + assert(_ctx && !broken()); + + const auto &tls_opts = opts.tls; + // If not compiled with TLS, TLS is always disabled. + if (tls::enabled(tls_opts)) { + _tls_ctx = tls::secure_connection(*_ctx, tls_opts); + } + + _set_options(); +} + +void Connection::reconnect() { + Connection connection(_opts); + + swap(*this, connection); +} + +void Connection::send(int argc, const char **argv, const std::size_t *argv_len) { + auto ctx = _context(); + + assert(ctx != nullptr); + + if (redisAppendCommandArgv(ctx, + argc, + argv, + argv_len) != REDIS_OK) { + throw_error(*ctx, "Failed to send command"); + } + + assert(!broken()); +} + +void Connection::send(CmdArgs &args) { + auto ctx = _context(); + + assert(ctx != nullptr); + + if (redisAppendCommandArgv(ctx, + args.size(), + args.argv(), + args.argv_len()) != REDIS_OK) { + throw_error(*ctx, "Failed to send command"); + } + + assert(!broken()); +} + +ReplyUPtr Connection::recv(bool handle_error_reply) { + auto *ctx = _context(); + + assert(ctx != nullptr); + + void *r = nullptr; + if (redisGetReply(ctx, &r) != REDIS_OK) { + throw_error(*ctx, "Failed to get reply"); + } + + assert(!broken() && r != nullptr); + + auto reply = ReplyUPtr(static_cast(r)); + + if (handle_error_reply && reply::is_error(*reply)) { + throw_error(*reply); + } + + return reply; +} + +void Connection::_set_options() { + _auth(); + + _select_db(); + + if (_opts.readonly) { + _enable_readonly(); + } +} + +void Connection::_enable_readonly() { + send("READONLY"); + + auto reply = recv(); + + assert(reply); + + reply::parse(*reply); +} + +void Connection::_auth() { + const std::string DEFAULT_USER = "default"; + + if (_opts.user == DEFAULT_USER && _opts.password.empty()) { + return; + } + + if (_opts.user == DEFAULT_USER) { + cmd::auth(*this, _opts.password); + } else { + // Redis 6.0 or latter + cmd::auth(*this, _opts.user, _opts.password); + } + + auto reply = recv(); + + assert(reply); + + reply::parse(*reply); +} + +void Connection::_select_db() { + if (_opts.db == 0) { + return; + } + + cmd::select(*this, _opts.db); + + auto reply = recv(); + + assert(reply); + + reply::parse(*reply); +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection.h new file mode 100644 index 000000000..340375561 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection.h @@ -0,0 +1,237 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_H +#define SEWENEW_REDISPLUSPLUS_CONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "reply.h" +#include "utils.h" +#include "tls.h" + +namespace sw { + +namespace redis { + +enum class ConnectionType { + TCP = 0, + UNIX +}; + +struct ConnectionOptions { +public: + ConnectionOptions() = default; + + explicit ConnectionOptions(const std::string &uri); + + ConnectionOptions(const ConnectionOptions &) = default; + ConnectionOptions& operator=(const ConnectionOptions &) = default; + + ConnectionOptions(ConnectionOptions &&) = default; + ConnectionOptions& operator=(ConnectionOptions &&) = default; + + ~ConnectionOptions() = default; + + ConnectionType type = ConnectionType::TCP; + + std::string host; + + int port = 6379; + + std::string path; + + std::string user = "default"; + + std::string password; + + int db = 0; + + bool keep_alive = false; + + std::chrono::milliseconds connect_timeout{0}; + + std::chrono::milliseconds socket_timeout{0}; + + tls::TlsOptions tls; + + // `readonly` is only used for reading from a slave node in Redis Cluster mode. + // Client code should never manually set/get it. This member might be removed in the future. + bool readonly = false; + +private: + ConnectionOptions _parse_uri(const std::string &uri) const; + + auto _split_uri(const std::string &uri) const + -> std::tuple; + + auto _split_path(const std::string &path) const + -> std::tuple; + + void _parse_parameters(const std::string ¶meter_string, + ConnectionOptions &opts) const; + + void _set_option(const std::string &key, const std::string &val, ConnectionOptions &opts) const; + + bool _parse_bool_option(const std::string &str) const; + + std::chrono::milliseconds _parse_timeout_option(const std::string &str) const; + + std::vector _split(const std::string &str, const std::string &delimiter) const; + + void _set_auth_opts(const std::string &auth, ConnectionOptions &opts) const; + + void _set_tcp_opts(const std::string &path, ConnectionOptions &opts) const; + + void _set_unix_opts(const std::string &path, ConnectionOptions &opts) const; +}; + +class CmdArgs; + +class Connection { +public: + explicit Connection(const ConnectionOptions &opts); + + Connection(const Connection &) = delete; + Connection& operator=(const Connection &) = delete; + + Connection(Connection &&) = default; + Connection& operator=(Connection &&) = default; + + ~Connection() = default; + + // Check if the connection is broken. Client needs to do this check + // before sending some command to the connection. If it's broken, + // client needs to reconnect it. + bool broken() const noexcept { + return _ctx->err != REDIS_OK; + } + + void reset() noexcept { + _ctx->err = 0; + } + + void invalidate() noexcept { + _ctx->err = REDIS_ERR; + } + + void reconnect(); + + auto create_time() const + -> std::chrono::time_point { + return _create_time; + } + + auto last_active() const + -> std::chrono::time_point { + return _last_active; + } + + template + void send(const char *format, Args &&...args); + + void send(int argc, const char **argv, const std::size_t *argv_len); + + void send(CmdArgs &args); + + ReplyUPtr recv(bool handle_error_reply = true); + + const ConnectionOptions& options() const { + return _opts; + } + + friend void swap(Connection &lhs, Connection &rhs) noexcept; + +private: + class Connector; + + struct ContextDeleter { + void operator()(redisContext *context) const { + if (context != nullptr) { + redisFree(context); + } + }; + }; + + using ContextUPtr = std::unique_ptr; + + void _set_options(); + + void _auth(); + + void _select_db(); + + void _enable_readonly(); + + redisContext* _context(); + + ContextUPtr _ctx; + + // The time that the connection is created. + std::chrono::time_point _create_time{}; + + // The time that the connection is created or the time that + // the connection is recently used, i.e. `_context()` is called. + std::chrono::time_point _last_active{}; + + ConnectionOptions _opts; + + // TODO: define _tls_ctx before _ctx + tls::TlsContextUPtr _tls_ctx; +}; + +using ConnectionSPtr = std::shared_ptr; + +enum class Role { + MASTER, + SLAVE +}; + +// Inline implementaions. + +template +inline void Connection::send(const char *format, Args &&...args) { + auto ctx = _context(); + + assert(ctx != nullptr); + + if (redisAppendCommand(ctx, + format, + std::forward(args)...) != REDIS_OK) { + throw_error(*ctx, "Failed to send command"); + } + + assert(!broken()); +} + +inline redisContext* Connection::_context() { + _last_active = std::chrono::steady_clock::now(); + + return _ctx.get(); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection_pool.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection_pool.cpp new file mode 100644 index 000000000..7049f5fc8 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection_pool.cpp @@ -0,0 +1,276 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "connection_pool.h" +#include +#include "errors.h" + +namespace sw { + +namespace redis { + +ConnectionPool::ConnectionPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts) : + _opts(connection_opts), + _pool_opts(pool_opts) { + if (_pool_opts.size == 0) { + throw Error("CANNOT create an empty pool"); + } + + // Lazily create connections. +} + +ConnectionPool::ConnectionPool(SimpleSentinel sentinel, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts) : + _opts(connection_opts), + _pool_opts(pool_opts), + _sentinel(std::move(sentinel)) { + // In this case, the connection must be of TCP type. + if (_opts.type != ConnectionType::TCP) { + throw Error("Sentinel only supports TCP connection"); + } + + if (_opts.connect_timeout == std::chrono::milliseconds(0) + || _opts.socket_timeout == std::chrono::milliseconds(0)) { + throw Error("With sentinel, connection timeout and socket timeout cannot be 0"); + } + + // Cleanup connection options. + _update_connection_opts("", -1); + + assert(_sentinel); +} + +ConnectionPool::ConnectionPool(ConnectionPool &&that) { + std::lock_guard lock(that._mutex); + + _move(std::move(that)); +} + +ConnectionPool& ConnectionPool::operator=(ConnectionPool &&that) { + if (this != &that) { + std::lock(_mutex, that._mutex); + std::lock_guard lock_this(_mutex, std::adopt_lock); + std::lock_guard lock_that(that._mutex, std::adopt_lock); + + _move(std::move(that)); + } + + return *this; +} + +Connection ConnectionPool::fetch() { + std::unique_lock lock(_mutex); + + if (_pool.empty()) { + if (_used_connections == _pool_opts.size) { + _wait_for_connection(lock); + } else { + // Lazily create a new connection. + auto connection = _create(); + + ++_used_connections; + + return connection; + } + } + + // _pool is NOT empty. + auto connection = _fetch(); + + auto connection_lifetime = _pool_opts.connection_lifetime; + auto connection_idle_time = _pool_opts.connection_idle_time; + + if (_sentinel) { + auto opts = _opts; + auto role_changed = _role_changed(connection.options()); + auto sentinel = _sentinel; + + lock.unlock(); + + if (role_changed || _need_reconnect(connection, connection_lifetime, connection_idle_time)) { + try { + connection = _create(sentinel, opts, false); + } catch (const Error &e) { + // Failed to reconnect, return it to the pool, and retry latter. + release(std::move(connection)); + throw; + } + } + + return connection; + } + + lock.unlock(); + + if (_need_reconnect(connection, connection_lifetime, connection_idle_time)) { + try { + connection.reconnect(); + } catch (const Error &e) { + // Failed to reconnect, return it to the pool, and retry latter. + release(std::move(connection)); + throw; + } + } + + return connection; +} + +ConnectionOptions ConnectionPool::connection_options() { + std::lock_guard lock(_mutex); + + return _opts; +} + +void ConnectionPool::release(Connection connection) { + { + std::lock_guard lock(_mutex); + + _pool.push_back(std::move(connection)); + } + + _cv.notify_one(); +} + +Connection ConnectionPool::create() { + std::unique_lock lock(_mutex); + + auto opts = _opts; + + if (_sentinel) { + auto sentinel = _sentinel; + + lock.unlock(); + + return _create(sentinel, opts, false); + } else { + lock.unlock(); + + return Connection(opts); + } +} + +ConnectionPool ConnectionPool::clone() { + std::unique_lock lock(_mutex); + + auto opts = _opts; + auto pool_opts = _pool_opts; + + if (_sentinel) { + auto sentinel = _sentinel; + + lock.unlock(); + + return ConnectionPool(sentinel, pool_opts, opts); + } else { + lock.unlock(); + + return ConnectionPool(pool_opts, opts); + } +} + +void ConnectionPool::_move(ConnectionPool &&that) { + _opts = std::move(that._opts); + _pool_opts = std::move(that._pool_opts); + _pool = std::move(that._pool); + _used_connections = that._used_connections; + _sentinel = std::move(that._sentinel); +} + +Connection ConnectionPool::_create() { + if (_sentinel) { + // Get Redis host and port info from sentinel. + return _create(_sentinel, _opts, true); + } + + return Connection(_opts); +} + +Connection ConnectionPool::_create(SimpleSentinel &sentinel, + const ConnectionOptions &opts, + bool locked) { + try { + auto connection = sentinel.create(opts); + + std::unique_lock lock(_mutex, std::defer_lock); + if (!locked) { + lock.lock(); + } + + const auto &connection_opts = connection.options(); + if (_role_changed(connection_opts)) { + // Master/Slave has been changed, reconnect all connections. + _update_connection_opts(connection_opts.host, connection_opts.port); + } + + return connection; + } catch (const StopIterError &e) { + throw Error("Failed to create connection with sentinel"); + } +} + +Connection ConnectionPool::_fetch() { + assert(!_pool.empty()); + + auto connection = std::move(_pool.front()); + _pool.pop_front(); + + return connection; +} + +void ConnectionPool::_wait_for_connection(std::unique_lock &lock) { + auto timeout = _pool_opts.wait_timeout; + if (timeout > std::chrono::milliseconds(0)) { + // Wait until _pool is no longer empty or timeout. + if (!_cv.wait_for(lock, + timeout, + [this] { return !(this->_pool).empty(); })) { + throw Error("Failed to fetch a connection in " + + std::to_string(timeout.count()) + " milliseconds"); + } + } else { + // Wait forever. + _cv.wait(lock, [this] { return !(this->_pool).empty(); }); + } +} + +bool ConnectionPool::_need_reconnect(const Connection &connection, + const std::chrono::milliseconds &connection_lifetime, + const std::chrono::milliseconds &connection_idle_time) const { + if (connection.broken()) { + return true; + } + + auto now = std::chrono::steady_clock::now(); + if (connection_lifetime > std::chrono::milliseconds(0)) { + if (now - connection.create_time() > connection_lifetime) { + return true; + } + } + + if (connection_idle_time > std::chrono::milliseconds(0)) { + if (now - connection.last_active() > connection_idle_time) { + return true; + } + } + + return false; +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection_pool.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection_pool.h new file mode 100644 index 000000000..30ba5daa2 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/connection_pool.h @@ -0,0 +1,182 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H +#define SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H + +#include +#include +#include +#include +#include +#include +#include "connection.h" +#include "sentinel.h" + +namespace sw { + +namespace redis { + +struct ConnectionPoolOptions { + // Max number of connections, including both in-use and idle ones. + std::size_t size = 1; + + // Max time to wait for a connection. 0ms means client waits forever. + std::chrono::milliseconds wait_timeout{0}; + + // Max lifetime of a connection. 0ms means we never expire the connection. + std::chrono::milliseconds connection_lifetime{0}; + + // Max idle time of a connection. 0ms means we never expire the connection. + std::chrono::milliseconds connection_idle_time{0}; +}; + +class ConnectionPool { +public: + ConnectionPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + ConnectionPool(SimpleSentinel sentinel, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + ConnectionPool() = default; + + ConnectionPool(ConnectionPool &&that); + ConnectionPool& operator=(ConnectionPool &&that); + + ConnectionPool(const ConnectionPool &) = delete; + ConnectionPool& operator=(const ConnectionPool &) = delete; + + ~ConnectionPool() = default; + + // Fetch a connection from pool. + Connection fetch(); + + ConnectionOptions connection_options(); + + void release(Connection connection); + + // Create a new connection. + Connection create(); + + ConnectionPool clone(); + +private: + void _move(ConnectionPool &&that); + + // NOT thread-safe + Connection _create(); + + Connection _create(SimpleSentinel &sentinel, const ConnectionOptions &opts, bool locked); + + Connection _fetch(); + + void _wait_for_connection(std::unique_lock &lock); + + bool _need_reconnect(const Connection &connection, + const std::chrono::milliseconds &connection_lifetime, + const std::chrono::milliseconds &connection_idle_time) const; + + void _update_connection_opts(const std::string &host, int port) { + _opts.host = host; + _opts.port = port; + } + + bool _role_changed(const ConnectionOptions &opts) const { + return opts.port != _opts.port || opts.host != _opts.host; + } + + ConnectionOptions _opts; + + ConnectionPoolOptions _pool_opts; + + std::deque _pool; + + std::size_t _used_connections = 0; + + std::mutex _mutex; + + std::condition_variable _cv; + + SimpleSentinel _sentinel; +}; + +using ConnectionPoolSPtr = std::shared_ptr; + +class SafeConnection { +public: + explicit SafeConnection(ConnectionPool &pool) : _pool(pool), _connection(_pool.fetch()) { + assert(!_connection.broken()); + } + + SafeConnection(const SafeConnection &) = delete; + SafeConnection& operator=(const SafeConnection &) = delete; + + SafeConnection(SafeConnection &&) = delete; + SafeConnection& operator=(SafeConnection &&) = delete; + + ~SafeConnection() { + _pool.release(std::move(_connection)); + } + + Connection& connection() { + return _connection; + } + +private: + ConnectionPool &_pool; + Connection _connection; +}; + +// NOTE: This class is similar to `SafeConnection`. +// The difference is that `SafeConnection` tries to avoid copying a std::shared_ptr. +class GuardedConnection { +public: + explicit GuardedConnection(const ConnectionPoolSPtr &pool) : _pool(pool), + _connection(_pool->fetch()) { + assert(!_connection.broken()); + } + + GuardedConnection(const GuardedConnection &) = delete; + GuardedConnection& operator=(const GuardedConnection &) = delete; + + GuardedConnection(GuardedConnection &&) = default; + GuardedConnection& operator=(GuardedConnection &&) = default; + + ~GuardedConnection() { + // If `GuardedConnection` has been moved, `_pool` will be nullptr. + if (_pool) { + _pool->release(std::move(_connection)); + } + } + + Connection& connection() { + return _connection; + } + +private: + ConnectionPoolSPtr _pool; + Connection _connection; +}; + +using GuardedConnectionSPtr = std::shared_ptr; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/crc16.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/crc16.cpp new file mode 100644 index 000000000..c94a08d30 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/crc16.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2001-2010 Georges Menie (www.menie.org) + * Copyright 2010-2012 Salvatore Sanfilippo (adapted to Redis coding style) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* CRC16 implementation according to CCITT standards. + * + * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the + * following parameters: + * + * Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN" + * Width : 16 bit + * Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1) + * Initialization : 0000 + * Reflect Input byte : False + * Reflect Output CRC : False + * Xor constant to output CRC : 0000 + * Output for "123456789" : 31C3 + */ + +#include + +namespace sw { + +namespace redis { + +static const uint16_t crc16tab[256]= { + 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, + 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, + 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, + 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, + 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, + 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, + 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, + 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, + 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, + 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, + 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, + 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, + 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, + 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, + 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, + 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, + 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, + 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, + 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, + 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, + 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, + 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, + 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, + 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, + 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, + 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, + 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, + 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, + 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, + 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, + 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, + 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 +}; + +uint16_t crc16(const char *buf, int len) { + int counter; + uint16_t crc = 0; + for (counter = 0; counter < len; counter++) + crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF]; + return crc; +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/cxx11/cxx_utils.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/cxx11/cxx_utils.h new file mode 100644 index 000000000..6134a6503 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/cxx11/cxx_utils.h @@ -0,0 +1,113 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CXX_UTILS_H +#define SEWENEW_REDISPLUSPLUS_CXX_UTILS_H + +#include +#include + +namespace sw { + +namespace redis { + +// By now, not all compilers support std::string_view and std::optional, +// so we make our own implementation. + +class StringView { +public: + constexpr StringView() noexcept = default; + + constexpr StringView(const char *data, std::size_t size) : _data(data), _size(size) {} + + StringView(const char *data) : _data(data), _size(std::strlen(data)) {} + + StringView(const std::string &str) : _data(str.data()), _size(str.size()) {} + + constexpr StringView(const StringView &) noexcept = default; + + StringView& operator=(const StringView &) noexcept = default; + + constexpr const char* data() const noexcept { + return _data; + } + + constexpr std::size_t size() const noexcept { + return _size; + } + +private: + const char *_data = nullptr; + std::size_t _size = 0; +}; + +template +class Optional { +public: +#if defined(_MSC_VER) && (_MSC_VER < 1910) + Optional() : _value() {} // MSVC 2015 bug +#else + Optional() = default; +#endif + + Optional(const Optional &) = default; + Optional& operator=(const Optional &) = default; + + Optional(Optional &&) = default; + Optional& operator=(Optional &&) = default; + + ~Optional() = default; + + template + explicit Optional(Args &&...args) : _value(true, T(std::forward(args)...)) {} + + explicit operator bool() const { + return _value.first; + } + + T& value() { + return _value.second; + } + + const T& value() const { + return _value.second; + } + + T* operator->() { + return &(_value.second); + } + + const T* operator->() const { + return &(_value.second); + } + + T& operator*() { + return _value.second; + } + + const T& operator*() const { + return _value.second; + } + +private: + std::pair _value; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CXX_UTILS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/cxx17/cxx_utils.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/cxx17/cxx_utils.h new file mode 100644 index 000000000..dce51abe9 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/cxx17/cxx_utils.h @@ -0,0 +1,46 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CXX_UTILS_H +#define SEWENEW_REDISPLUSPLUS_CXX_UTILS_H + +#include +#include +#include + +#define REDIS_PLUS_PLUS_HAS_OPTIONAL + +#define REDIS_PLUS_PLUS_HAS_VARIANT + +namespace sw { + +namespace redis { + +using StringView = std::string_view; + +template +using Optional = std::optional; + +template +using Variant = std::variant; + +using Monostate = std::monostate; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CXX_UTILS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/errors.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/errors.cpp new file mode 100644 index 000000000..23d2c3874 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/errors.cpp @@ -0,0 +1,142 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "errors.h" +#include +#include +#include +#include +#include "shards.h" + +namespace { + +using namespace sw::redis; + +std::pair parse_error(const std::string &msg); + +std::unordered_map error_map = { + {"MOVED", ReplyErrorType::MOVED}, + {"ASK", ReplyErrorType::ASK} +}; + +} + +namespace sw { + +namespace redis { + +void throw_error(const redisContext &context, const std::string &err_info) { + auto err_code = context.err; + const auto *err_str = context.errstr; + if (err_str == nullptr) { + throw Error(err_info + ": null error message: " + std::to_string(err_code)); + } + + auto err_msg = err_info + ": " + err_str; + + switch (err_code) { + case REDIS_ERR_IO: + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ETIMEDOUT) { + throw TimeoutError(err_msg); + } else { + throw IoError(err_msg); + } + break; + + case REDIS_ERR_EOF: + throw ClosedError(err_msg); + break; + + case REDIS_ERR_PROTOCOL: + throw ProtoError(err_msg); + break; + + case REDIS_ERR_OOM: + throw OomError(err_msg); + break; + + case REDIS_ERR_OTHER: + throw Error(err_msg); + break; + +#ifdef REDIS_ERR_TIMEOUT + case REDIS_ERR_TIMEOUT: + throw TimeoutError(err_msg); + break; +#endif + + default: + throw Error("unknown error code: " + err_msg); + } +} + +void throw_error(const redisReply &reply) { + assert(reply.type == REDIS_REPLY_ERROR); + + if (reply.str == nullptr) { + throw Error("Null error reply"); + } + + auto err_str = std::string(reply.str, reply.len); + + auto err_type = ReplyErrorType::ERR; + std::string err_msg; + std::tie(err_type, err_msg) = parse_error(err_str); + + switch (err_type) { + case ReplyErrorType::MOVED: + throw MovedError(err_msg); + break; + + case ReplyErrorType::ASK: + throw AskError(err_msg); + break; + + default: + throw ReplyError(err_str); + break; + } +} + +} + +} + +namespace { + +using namespace sw::redis; + +std::pair parse_error(const std::string &err) { + // The error contains an Error Prefix, and an optional error message. + auto idx = err.find_first_of(" \n"); + + if (idx == std::string::npos) { + throw ProtoError("No Error Prefix: " + err); + } + + auto err_prefix = err.substr(0, idx); + auto err_type = ReplyErrorType::ERR; + + auto iter = error_map.find(err_prefix); + if (iter != error_map.end()) { + // Specific error. + err_type = iter->second; + } // else Generic error. + + return {err_type, err.substr(idx + 1)}; +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/errors.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/errors.h new file mode 100644 index 000000000..7cc19d2f1 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/errors.h @@ -0,0 +1,166 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ERRORS_H +#define SEWENEW_REDISPLUSPLUS_ERRORS_H + +#include +#include +#include + +namespace sw { + +namespace redis { + +enum ReplyErrorType { + ERR, + MOVED, + ASK +}; + +class Error : public std::exception { +public: + explicit Error(const std::string &msg) : _msg(msg) {} + + Error(const Error &) = default; + Error& operator=(const Error &) = default; + + Error(Error &&) = default; + Error& operator=(Error &&) = default; + + virtual ~Error() override = default; + + virtual const char* what() const noexcept override { + return _msg.data(); + } + +private: + std::string _msg; +}; + +class IoError : public Error { +public: + explicit IoError(const std::string &msg) : Error(msg) {} + + IoError(const IoError &) = default; + IoError& operator=(const IoError &) = default; + + IoError(IoError &&) = default; + IoError& operator=(IoError &&) = default; + + virtual ~IoError() override = default; +}; + +class TimeoutError : public IoError { +public: + explicit TimeoutError(const std::string &msg) : IoError(msg) {} + + TimeoutError(const TimeoutError &) = default; + TimeoutError& operator=(const TimeoutError &) = default; + + TimeoutError(TimeoutError &&) = default; + TimeoutError& operator=(TimeoutError &&) = default; + + virtual ~TimeoutError() override = default; +}; + +class ClosedError : public Error { +public: + explicit ClosedError(const std::string &msg) : Error(msg) {} + + ClosedError(const ClosedError &) = default; + ClosedError& operator=(const ClosedError &) = default; + + ClosedError(ClosedError &&) = default; + ClosedError& operator=(ClosedError &&) = default; + + virtual ~ClosedError() override = default; +}; + +class ProtoError : public Error { +public: + explicit ProtoError(const std::string &msg) : Error(msg) {} + + ProtoError(const ProtoError &) = default; + ProtoError& operator=(const ProtoError &) = default; + + ProtoError(ProtoError &&) = default; + ProtoError& operator=(ProtoError &&) = default; + + virtual ~ProtoError() override = default; +}; + +class OomError : public Error { +public: + explicit OomError(const std::string &msg) : Error(msg) {} + + OomError(const OomError &) = default; + OomError& operator=(const OomError &) = default; + + OomError(OomError &&) = default; + OomError& operator=(OomError &&) = default; + + virtual ~OomError() override = default; +}; + +class ReplyError : public Error { +public: + explicit ReplyError(const std::string &msg) : Error(msg) {} + + ReplyError(const ReplyError &) = default; + ReplyError& operator=(const ReplyError &) = default; + + ReplyError(ReplyError &&) = default; + ReplyError& operator=(ReplyError &&) = default; + + virtual ~ReplyError() override = default; +}; + +class WatchError : public Error { +public: + explicit WatchError() : Error("Watched key has been modified") {} + + WatchError(const WatchError &) = default; + WatchError& operator=(const WatchError &) = default; + + WatchError(WatchError &&) = default; + WatchError& operator=(WatchError &&) = default; + + virtual ~WatchError() override = default; +}; + + +// MovedError and AskError are defined in shards.h +class MovedError; + +class AskError; + +void throw_error(const redisContext &context, const std::string &err_info); + +void throw_error(const redisReply &reply); + +template +inline void range_check(const char *cmd, Input first, Input last) { + if (first == last) { + throw Error(std::string(cmd) + ": no key specified"); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ERRORS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/event_loop.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/event_loop.cpp new file mode 100644 index 000000000..8e210b883 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/event_loop.cpp @@ -0,0 +1,273 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "event_loop.h" +#include +#include +#include "async_connection.h" + +namespace sw { + +namespace redis { + +EventLoop::EventLoop() { + _loop = _create_event_loop(); + + _event_async = _create_uv_async(_event_callback); + _stop_async = _create_uv_async(_stop_callback); + + _loop_thread = std::thread([this]() { uv_run(this->_loop.get(), UV_RUN_DEFAULT); }); +} + +EventLoop::~EventLoop() { + _stop(); + + if (_loop_thread.joinable()) { + _loop_thread.join(); + } +} + +void EventLoop::unwatch(AsyncConnectionSPtr connection, std::exception_ptr err) { + assert(connection); + + { + std::lock_guard lock(_mtx); + + _disconnect_events.emplace(std::move(connection), err); + } + + _notify(); +} + +void EventLoop::add(AsyncConnectionSPtr event) { + assert(event); + + { + std::lock_guard lock(_mtx); + + _command_events.insert(std::move(event)); + } + + _notify(); +} + +void EventLoop::watch(redisAsyncContext &ctx) { + if (redisLibuvAttach(&ctx, _loop.get()) != REDIS_OK) { + throw Error("failed to attach to event loop"); + } + + redisAsyncSetConnectCallback(&ctx, EventLoop::_connect_callback); + redisAsyncSetDisconnectCallback(&ctx, EventLoop::_disconnect_callback); +} + +void EventLoop::_connect_callback(const redisAsyncContext *ctx, int status) { + assert(ctx != nullptr); + + auto *context = static_cast(ctx->data); + assert(context != nullptr); + + auto &connection = context->connection; + std::exception_ptr err; + if (status != REDIS_OK) { + try { + throw_error(ctx->c, "failed to connect to server"); + } catch (const Error &e) { + err = std::current_exception(); + } + } + + connection->connect_callback(err); +} + +void EventLoop::_disconnect_callback(const redisAsyncContext *ctx, int status) { + assert(ctx != nullptr); + + auto *context = static_cast(ctx->data); + assert(context != nullptr); + + if (!context->run_disconnect_callback) { + return; + } + + auto &connection = context->connection; + std::exception_ptr err; + if (status != REDIS_OK) { + try { + throw_error(ctx->c, "failed to disconnect from server"); + } catch (const Error &e) { + err = std::current_exception(); + } + } + + // TODO: if status == REDIS_OK, should we call the callback? + connection->disconnect_callback(err); +} + +void EventLoop::_event_callback(uv_async_t *handle) { + assert(handle != nullptr); + + auto *event_loop = static_cast(handle->data); + assert(event_loop != nullptr); + + std::unordered_set command_events; + std::unordered_map disconnect_events; + std::tie(command_events, disconnect_events) = event_loop->_get_events(); + + for (auto &connection : command_events) { + assert(connection); + + connection->event_callback(); + } + + for (auto &ele : disconnect_events) { + auto &connection = ele.first; + auto &err = ele.second; + + assert(connection); + + if (!err) { + // Ensure all pending events have been sent before disconnecting. + connection->event_callback(); + } + + // If `event_callback` fails, connection will be release by event loop, + // and this `disconnect` call will do nothing. + connection->disconnect(err); + } +} + +void EventLoop::_stop_callback(uv_async_t *handle) { + assert(handle != nullptr); + + auto *event_loop = static_cast(handle->data); + assert(event_loop != nullptr); + + std::unordered_set command_events; + std::unordered_map disconnect_events; + std::tie(command_events, disconnect_events) = event_loop->_get_events(); + + event_loop->_clean_up(command_events, disconnect_events); + + uv_stop(event_loop->_loop.get()); +} + +void EventLoop::_clean_up(std::unordered_set &command_events, + std::unordered_map &disconnect_events) { + auto err = std::make_exception_ptr(Error("event loop is closing")); + for (auto &connection : command_events) { + assert(connection); + + connection->disconnect(err); + } + + for (auto &ele : disconnect_events) { + auto &connection = ele.first; + auto e = ele.second; + if (!e) { + e = err; + } + + assert(connection); + + connection->disconnect(e); + } +} + +void EventLoop::LoopDeleter::operator()(uv_loop_t *loop) const { + if (loop == nullptr) { + return; + } + + // How to correctly close an event loop: + // https://stackoverflow.com/questions/25615340/closing-libuv-handles-correctly + // TODO: do we need to call this? Since we always has 2 async_t handles. + if (uv_loop_close(loop) == 0) { + delete loop; + + return; + } + + uv_walk(loop, + [](uv_handle_t *handle, void *) { + if (handle != nullptr) { + // We don't need to release handle's memory in close callback, + // since we'll release the memory in EventLoop's destructor. + uv_close(handle, nullptr); + } + }, + nullptr); + + // Ensure uv_walk's callback to be called. + uv_run(loop, UV_RUN_DEFAULT); + + uv_loop_close(loop); + + delete loop; +} + +void EventLoop::_notify() { + assert(_event_async); + + uv_async_send(_event_async.get()); +} + +void EventLoop::_stop() { + assert(_stop_async); + + uv_async_send(_stop_async.get()); +} + +auto EventLoop::_get_events() + -> std::pair, + std::unordered_map> { + std::unordered_set command_events; + std::unordered_map disconnect_events; + { + std::lock_guard lock(_mtx); + + command_events.swap(_command_events); + disconnect_events.swap(_disconnect_events); + } + + return std::make_pair(std::move(command_events), std::move(disconnect_events)); +} + +EventLoop::UvAsyncUPtr EventLoop::_create_uv_async(AsyncCallback callback) { + auto uv_async = std::unique_ptr(new uv_async_t); + auto err = uv_async_init(_loop.get(), uv_async.get(), callback); + if (err != 0) { + throw Error("failed to initialize async: " + _err_msg(err)); + } + + uv_async->data = this; + + return uv_async; +} + +EventLoop::LoopUPtr EventLoop::_create_event_loop() const { + auto *loop = new uv_loop_t; + auto err = uv_loop_init(loop); + if (err != 0) { + delete loop; + throw Error("failed to initialize event loop: " + _err_msg(err)); + } + + return LoopUPtr(loop); +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/event_loop.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/event_loop.h new file mode 100644 index 000000000..7df87770a --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/event_loop.h @@ -0,0 +1,119 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_EVENT_LOOP_H +#define SEWENEW_REDISPLUSPLUS_EVENT_LOOP_H + +#include +#include +#include +#include +#include +#include +#include +#include "connection.h" + +namespace sw { + +namespace redis { + +class AsyncConnection; +class AsyncEvent; + +class EventLoop { +public: + EventLoop(); + + EventLoop(const EventLoop &) = delete; + EventLoop& operator=(const EventLoop &) = delete; + + EventLoop(EventLoop &&that); + + EventLoop& operator=(EventLoop &&that); + + ~EventLoop(); + + void unwatch(std::shared_ptr connection, std::exception_ptr err = nullptr); + + void add(std::shared_ptr event); + + // Not thread safe. Only call it in callback functions. + void watch(redisAsyncContext &ctx); + +private: + static void _connect_callback(const redisAsyncContext *ctx, int status); + + static void _disconnect_callback(const redisAsyncContext *ctx, int status); + + static void _event_callback(uv_async_t *handle); + + static void _stop_callback(uv_async_t *handle); + + struct LoopDeleter { + void operator()(uv_loop_t *loop) const; + }; + + using LoopUPtr = std::unique_ptr; + + std::string _err_msg(int err) const { + return uv_strerror(err); + } + + LoopUPtr _create_event_loop() const; + + using UvAsyncUPtr = std::unique_ptr; + + using AsyncCallback = void (*)(uv_async_t*); + + UvAsyncUPtr _create_uv_async(AsyncCallback callback); + + void _stop(); + + void _notify(); + + void _clean_up(std::unordered_set> &command_events, + std::unordered_map, std::exception_ptr> &disconnect_events); + + auto _get_events() + -> std::pair>, + std::unordered_map, std::exception_ptr>>; + + // We must define _event_async and _stop_async before _loop, + // because these memory can only be release after _loop's deleter + // has been called, i.e. the deleter will close these handles. + UvAsyncUPtr _event_async; + + UvAsyncUPtr _stop_async; + + std::thread _loop_thread; + + std::mutex _mtx; + + std::unordered_map, std::exception_ptr> _disconnect_events; + + std::unordered_set> _command_events; + + // _loop must be defined at last, since its destructor needs other data members. + LoopUPtr _loop; +}; + +using EventLoopSPtr = std::shared_ptr; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_EVENT_LOOP_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/future/boost/async_utils.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/future/boost/async_utils.h new file mode 100644 index 000000000..7445d5d0c --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/future/boost/async_utils.h @@ -0,0 +1,39 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ASYNC_UTILS_H +#define SEWENEW_REDISPLUSPLUS_ASYNC_UTILS_H + +#define BOOST_THREAD_PROVIDES_FUTURE +#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION + +#include + +namespace sw { + +namespace redis { + +template +using Future = boost::future; + +template +using Promise = boost::promise; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ASYNC_UTILS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/future/std/async_utils.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/future/std/async_utils.h new file mode 100644 index 000000000..0a5afedfe --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/future/std/async_utils.h @@ -0,0 +1,36 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ASYNC_UTILS_H +#define SEWENEW_REDISPLUSPLUS_ASYNC_UTILS_H + +#include + +namespace sw { + +namespace redis { + +template +using Future = std::future; + +template +using Promise = std::promise; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ASYNC_UTILS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/no_tls/tls.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/no_tls/tls.h new file mode 100644 index 000000000..0f2303db7 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/no_tls/tls.h @@ -0,0 +1,47 @@ +/************************************************************************** + Copyright (c) 2020 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_NO_TLS_H +#define SEWENEW_REDISPLUSPLUS_NO_TLS_H + +#include + +namespace sw { + +namespace redis { + +namespace tls { + +struct TlsOptions {}; + +struct TlsContextUPtr {}; + +inline TlsContextUPtr secure_connection(redisContext &/*ctx*/, const TlsOptions &/*opts*/) { + // Do nothing + return {}; +} + +inline bool enabled(const TlsOptions &/*opts*/) { + return false; +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_NO_TLS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/pipeline.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/pipeline.cpp new file mode 100644 index 000000000..24fe9cace --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/pipeline.cpp @@ -0,0 +1,35 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "pipeline.h" + +namespace sw { + +namespace redis { + +std::vector PipelineImpl::exec(Connection &connection, std::size_t cmd_num) { + std::vector replies; + while (cmd_num > 0) { + replies.push_back(connection.recv(false)); + --cmd_num; + } + + return replies; +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/pipeline.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/pipeline.h new file mode 100644 index 000000000..52b01253f --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/pipeline.h @@ -0,0 +1,49 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_PIPELINE_H +#define SEWENEW_REDISPLUSPLUS_PIPELINE_H + +#include +#include +#include "connection.h" + +namespace sw { + +namespace redis { + +class PipelineImpl { +public: + template + void command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + } + + std::vector exec(Connection &connection, std::size_t cmd_num); + + void discard(Connection &connection, std::size_t /*cmd_num*/) { + // Reconnect to Redis to discard all commands. + connection.reconnect(); + } +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_PIPELINE_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/queued_redis.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/queued_redis.h new file mode 100644 index 000000000..e663c7423 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/queued_redis.h @@ -0,0 +1,2013 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H +#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H + +#include +#include +#include +#include +#include +#include "connection.h" +#include "connection_pool.h" +#include "utils.h" +#include "reply.h" +#include "command.h" +#include "redis.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class QueuedReplies; + +// If any command throws, QueuedRedis resets the connection, and becomes invalid. +// In this case, the only thing we can do is to destory the QueuedRedis object. +template +class QueuedRedis { +public: + QueuedRedis(QueuedRedis &&) = default; + QueuedRedis& operator=(QueuedRedis &&) = default; + + // When it destructs, the underlying *Connection* will be closed or return to pool, + // and any command that has NOT been executed will be ignored. + ~QueuedRedis(); + + Redis redis(); + + template + auto command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, + QueuedRedis&>::type; + + template + QueuedRedis& command(const StringView &cmd_name, Args &&...args); + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, QueuedRedis&>::type; + + QueuedReplies exec(); + + void discard(); + + // CONNECTION commands. + + QueuedRedis& auth(const StringView &password) { + return command(cmd::auth, password); + } + + QueuedRedis& auth(const StringView &user, const StringView &password) { + return command( + cmd::auth, user, password); + } + + QueuedRedis& echo(const StringView &msg) { + return command(cmd::echo, msg); + } + + QueuedRedis& ping() { + return command(cmd::ping); + } + + QueuedRedis& ping(const StringView &msg) { + return command(cmd::ping, msg); + } + + // We DO NOT support the QUIT command. See *Redis::quit* doc for details. + // + // QueuedRedis& quit(); + + QueuedRedis& select(long long idx) { + return command(cmd::select, idx); + } + + QueuedRedis& swapdb(long long idx1, long long idx2) { + return command(cmd::swapdb, idx1, idx2); + } + + // SERVER commands. + + QueuedRedis& bgrewriteaof() { + return command(cmd::bgrewriteaof); + } + + QueuedRedis& bgsave() { + return command(cmd::bgsave); + } + + QueuedRedis& dbsize() { + return command(cmd::dbsize); + } + + QueuedRedis& flushall(bool async = false) { + return command(cmd::flushall, async); + } + + QueuedRedis& flushdb(bool async = false) { + return command(cmd::flushdb, async); + } + + QueuedRedis& info() { + return command(cmd::info); + } + + QueuedRedis& info(const StringView §ion) { + return command(cmd::info, section); + } + + QueuedRedis& lastsave() { + return command(cmd::lastsave); + } + + QueuedRedis& save() { + return command(cmd::save); + } + + // KEY commands. + + QueuedRedis& del(const StringView &key) { + return command(cmd::del, key); + } + + template + QueuedRedis& del(Input first, Input last) { + range_check("DEL", first, last); + + return command(cmd::del_range, first, last); + } + + template + QueuedRedis& del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + QueuedRedis& dump(const StringView &key) { + return command(cmd::dump, key); + } + + QueuedRedis& exists(const StringView &key) { + return command(cmd::exists, key); + } + + template + QueuedRedis& exists(Input first, Input last) { + range_check("EXISTS", first, last); + + return command(cmd::exists_range, first, last); + } + + template + QueuedRedis& exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + QueuedRedis& expire(const StringView &key, long long timeout) { + return command(cmd::expire, key, timeout); + } + + QueuedRedis& expire(const StringView &key, + const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); + } + + QueuedRedis& expireat(const StringView &key, long long timestamp) { + return command(cmd::expireat, key, timestamp); + } + + QueuedRedis& expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); + } + + QueuedRedis& keys(const StringView &pattern) { + return command(cmd::keys, pattern); + } + + QueuedRedis& move(const StringView &key, long long db) { + return command(cmd::move, key, db); + } + + QueuedRedis& persist(const StringView &key) { + return command(cmd::persist, key); + } + + QueuedRedis& pexpire(const StringView &key, long long timeout) { + return command(cmd::pexpire, key, timeout); + } + + QueuedRedis& pexpire(const StringView &key, + const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); + } + + QueuedRedis& pexpireat(const StringView &key, long long timestamp) { + return command(cmd::pexpireat, key, timestamp); + } + + QueuedRedis& pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); + } + + QueuedRedis& pttl(const StringView &key) { + return command(cmd::pttl, key); + } + + QueuedRedis& randomkey() { + return command(cmd::randomkey); + } + + QueuedRedis& rename(const StringView &key, const StringView &newkey) { + return command(cmd::rename, key, newkey); + } + + QueuedRedis& renamenx(const StringView &key, const StringView &newkey) { + return command(cmd::renamenx, key, newkey); + } + + QueuedRedis& restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false) { + return command(cmd::restore, key, val, ttl, replace); + } + + QueuedRedis& restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false) { + return restore(key, val, ttl.count(), replace); + } + + // TODO: sort + + QueuedRedis& scan(long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::scan, cursor, pattern, count); + } + + QueuedRedis& scan(long long cursor) { + return scan(cursor, "*", 10); + } + + QueuedRedis& scan(long long cursor, + const StringView &pattern) { + return scan(cursor, pattern, 10); + } + + QueuedRedis& scan(long long cursor, + long long count) { + return scan(cursor, "*", count); + } + + QueuedRedis& touch(const StringView &key) { + return command(cmd::touch, key); + } + + template + QueuedRedis& touch(Input first, Input last) { + range_check("TOUCH", first, last); + + return command(cmd::touch_range, first, last); + } + + template + QueuedRedis& touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + QueuedRedis& ttl(const StringView &key) { + return command(cmd::ttl, key); + } + + QueuedRedis& type(const StringView &key) { + return command(cmd::type, key); + } + + QueuedRedis& unlink(const StringView &key) { + return command(cmd::unlink, key); + } + + template + QueuedRedis& unlink(Input first, Input last) { + range_check("UNLINK", first, last); + + return command(cmd::unlink_range, first, last); + } + + template + QueuedRedis& unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + QueuedRedis& wait(long long numslaves, long long timeout) { + return command(cmd::wait, numslaves, timeout); + } + + QueuedRedis& wait(long long numslaves, const std::chrono::milliseconds &timeout) { + return wait(numslaves, timeout.count()); + } + + // STRING commands. + + QueuedRedis& append(const StringView &key, const StringView &str) { + return command(cmd::append, key, str); + } + + QueuedRedis& bitcount(const StringView &key, + long long start = 0, + long long end = -1) { + return command(cmd::bitcount, key, start, end); + } + + QueuedRedis& bitop(BitOp op, + const StringView &destination, + const StringView &key) { + return command(cmd::bitop, op, destination, key); + } + + template + QueuedRedis& bitop(BitOp op, + const StringView &destination, + Input first, + Input last) { + range_check("BITOP", first, last); + + return command(cmd::bitop_range, op, destination, first, last); + } + + template + QueuedRedis& bitop(BitOp op, + const StringView &destination, + std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + QueuedRedis& bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1) { + return command(cmd::bitpos, key, bit, start, end); + } + + QueuedRedis& decr(const StringView &key) { + return command(cmd::decr, key); + } + + QueuedRedis& decrby(const StringView &key, long long decrement) { + return command(cmd::decrby, key, decrement); + } + + QueuedRedis& get(const StringView &key) { + return command(cmd::get, key); + } + + QueuedRedis& getbit(const StringView &key, long long offset) { + return command(cmd::getbit, key, offset); + } + + QueuedRedis& getrange(const StringView &key, long long start, long long end) { + return command(cmd::getrange, key, start, end); + } + + QueuedRedis& getset(const StringView &key, const StringView &val) { + return command(cmd::getset, key, val); + } + + QueuedRedis& incr(const StringView &key) { + return command(cmd::incr, key); + } + + QueuedRedis& incrby(const StringView &key, long long increment) { + return command(cmd::incrby, key, increment); + } + + QueuedRedis& incrbyfloat(const StringView &key, double increment) { + return command(cmd::incrbyfloat, key, increment); + } + + template + QueuedRedis& mget(Input first, Input last) { + range_check("MGET", first, last); + + return command(cmd::mget, first, last); + } + + template + QueuedRedis& mget(std::initializer_list il) { + return mget(il.begin(), il.end()); + } + + template + QueuedRedis& mset(Input first, Input last) { + range_check("MSET", first, last); + + return command(cmd::mset, first, last); + } + + template + QueuedRedis& mset(std::initializer_list il) { + return mset(il.begin(), il.end()); + } + + template + QueuedRedis& msetnx(Input first, Input last) { + range_check("MSETNX", first, last); + + return command(cmd::msetnx, first, last); + } + + template + QueuedRedis& msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + QueuedRedis& psetex(const StringView &key, + long long ttl, + const StringView &val) { + return command(cmd::psetex, key, ttl, val); + } + + QueuedRedis& psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); + } + + QueuedRedis& set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS) { + _set_cmd_indexes.push_back(_cmd_num); + + return command(cmd::set, key, val, ttl.count(), type); + } + + QueuedRedis& setex(const StringView &key, + long long ttl, + const StringView &val) { + return command(cmd::setex, key, ttl, val); + } + + QueuedRedis& setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + return setex(key, ttl.count(), val); + } + + QueuedRedis& setnx(const StringView &key, const StringView &val) { + return command(cmd::setnx, key, val); + } + + QueuedRedis& setrange(const StringView &key, + long long offset, + const StringView &val) { + return command(cmd::setrange, key, offset, val); + } + + QueuedRedis& strlen(const StringView &key) { + return command(cmd::strlen, key); + } + + // LIST commands. + + QueuedRedis& blpop(const StringView &key, long long timeout) { + return command(cmd::blpop, key, timeout); + } + + QueuedRedis& blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(key, timeout.count()); + } + + template + QueuedRedis& blpop(Input first, Input last, long long timeout) { + range_check("BLPOP", first, last); + + return command(cmd::blpop_range, first, last, timeout); + } + + template + QueuedRedis& blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(first, last, timeout.count()); + } + + template + QueuedRedis& blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + QueuedRedis& brpop(const StringView &key, long long timeout) { + return command(cmd::brpop, key, timeout); + } + + QueuedRedis& brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(key, timeout.count()); + } + + template + QueuedRedis& brpop(Input first, Input last, long long timeout) { + range_check("BRPOP", first, last); + + return command(cmd::brpop_range, first, last, timeout); + } + + template + QueuedRedis& brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(first, last, timeout.count()); + } + + template + QueuedRedis& brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + QueuedRedis& brpoplpush(const StringView &source, + const StringView &destination, + long long timeout) { + return command(cmd::brpoplpush, source, destination, timeout); + } + + QueuedRedis& brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpoplpush(source, destination, timeout.count()); + } + + QueuedRedis& lindex(const StringView &key, long long index) { + return command(cmd::lindex, key, index); + } + + QueuedRedis& linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val) { + return command(cmd::linsert, key, position, pivot, val); + } + + QueuedRedis& llen(const StringView &key) { + return command(cmd::llen, key); + } + + QueuedRedis& lpop(const StringView &key) { + return command(cmd::lpop, key); + } + + QueuedRedis& lpush(const StringView &key, const StringView &val) { + return command(cmd::lpush, key, val); + } + + template + QueuedRedis& lpush(const StringView &key, Input first, Input last) { + range_check("LPUSH", first, last); + + return command(cmd::lpush_range, key, first, last); + } + + template + QueuedRedis& lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + QueuedRedis& lpushx(const StringView &key, const StringView &val) { + return command(cmd::lpushx, key, val); + } + + QueuedRedis& lrange(const StringView &key, + long long start, + long long stop) { + return command(cmd::lrange, key, start, stop); + } + + QueuedRedis& lrem(const StringView &key, long long count, const StringView &val) { + return command(cmd::lrem, key, count, val); + } + + QueuedRedis& lset(const StringView &key, long long index, const StringView &val) { + return command(cmd::lset, key, index, val); + } + + QueuedRedis& ltrim(const StringView &key, long long start, long long stop) { + return command(cmd::ltrim, key, start, stop); + } + + QueuedRedis& rpop(const StringView &key) { + return command(cmd::rpop, key); + } + + QueuedRedis& rpoplpush(const StringView &source, const StringView &destination) { + return command(cmd::rpoplpush, source, destination); + } + + QueuedRedis& rpush(const StringView &key, const StringView &val) { + return command(cmd::rpush, key, val); + } + + template + QueuedRedis& rpush(const StringView &key, Input first, Input last) { + range_check("RPUSH", first, last); + + return command(cmd::rpush_range, key, first, last); + } + + template + QueuedRedis& rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + QueuedRedis& rpushx(const StringView &key, const StringView &val) { + return command(cmd::rpushx, key, val); + } + + // HASH commands. + + QueuedRedis& hdel(const StringView &key, const StringView &field) { + return command(cmd::hdel, key, field); + } + + template + QueuedRedis& hdel(const StringView &key, Input first, Input last) { + range_check("HDEL", first, last); + + return command(cmd::hdel_range, key, first, last); + } + + template + QueuedRedis& hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + QueuedRedis& hexists(const StringView &key, const StringView &field) { + return command(cmd::hexists, key, field); + } + + QueuedRedis& hget(const StringView &key, const StringView &field) { + return command(cmd::hget, key, field); + } + + QueuedRedis& hgetall(const StringView &key) { + return command(cmd::hgetall, key); + } + + QueuedRedis& hincrby(const StringView &key, + const StringView &field, + long long increment) { + return command(cmd::hincrby, key, field, increment); + } + + QueuedRedis& hincrbyfloat(const StringView &key, + const StringView &field, + double increment) { + return command(cmd::hincrbyfloat, key, field, increment); + } + + QueuedRedis& hkeys(const StringView &key) { + return command(cmd::hkeys, key); + } + + QueuedRedis& hlen(const StringView &key) { + return command(cmd::hlen, key); + } + + template + QueuedRedis& hmget(const StringView &key, Input first, Input last) { + range_check("HMGET", first, last); + + return command(cmd::hmget, key, first, last); + } + + template + QueuedRedis& hmget(const StringView &key, std::initializer_list il) { + return hmget(key, il.begin(), il.end()); + } + + template + QueuedRedis& hmset(const StringView &key, Input first, Input last) { + range_check("HMSET", first, last); + + return command(cmd::hmset, key, first, last); + } + + template + QueuedRedis& hmset(const StringView &key, std::initializer_list il) { + return hmset(key, il.begin(), il.end()); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::hscan, key, cursor, pattern, count); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return hscan(key, cursor, pattern, 10); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + long long count) { + return hscan(key, cursor, "*", count); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor) { + return hscan(key, cursor, "*", 10); + } + + QueuedRedis& hset(const StringView &key, const StringView &field, const StringView &val) { + return command(cmd::hset, key, field, val); + } + + QueuedRedis& hset(const StringView &key, const std::pair &item) { + return hset(key, item.first, item.second); + } + + template + auto hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("HSET", first, last); + + return command(cmd::hset_range, key, first, last); + } + + template + QueuedRedis& hset(const StringView &key, std::initializer_list il) { + return hset(key, il.begin(), il.end()); + } + + QueuedRedis& hsetnx(const StringView &key, const StringView &field, const StringView &val) { + return command(cmd::hsetnx, key, field, val); + } + + QueuedRedis& hsetnx(const StringView &key, const std::pair &item) { + return hsetnx(key, item.first, item.second); + } + + QueuedRedis& hstrlen(const StringView &key, const StringView &field) { + return command(cmd::hstrlen, key, field); + } + + QueuedRedis& hvals(const StringView &key) { + return command(cmd::hvals, key); + } + + // SET commands. + + QueuedRedis& sadd(const StringView &key, const StringView &member) { + return command(cmd::sadd, key, member); + } + + template + QueuedRedis& sadd(const StringView &key, Input first, Input last) { + range_check("SADD", first, last); + + return command(cmd::sadd_range, key, first, last); + } + + template + QueuedRedis& sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + QueuedRedis& scard(const StringView &key) { + return command(cmd::scard, key); + } + + template + QueuedRedis& sdiff(Input first, Input last) { + range_check("SDIFF", first, last); + + return command(cmd::sdiff, first, last); + } + + template + QueuedRedis& sdiff(std::initializer_list il) { + return sdiff(il.begin(), il.end()); + } + + QueuedRedis& sdiffstore(const StringView &destination, const StringView &key) { + return command(cmd::sdiffstore, destination, key); + } + + template + QueuedRedis& sdiffstore(const StringView &destination, + Input first, + Input last) { + range_check("SDIFFSTORE", first, last); + + return command(cmd::sdiffstore_range, destination, first, last); + } + + template + QueuedRedis& sdiffstore(const StringView &destination, std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + QueuedRedis& sinter(Input first, Input last) { + range_check("SINTER", first, last); + + return command(cmd::sinter, first, last); + } + + template + QueuedRedis& sinter(std::initializer_list il) { + return sinter(il.begin(), il.end()); + } + + QueuedRedis& sinterstore(const StringView &destination, const StringView &key) { + return command(cmd::sinterstore, destination, key); + } + + template + QueuedRedis& sinterstore(const StringView &destination, + Input first, + Input last) { + range_check("SINTERSTORE", first, last); + + return command(cmd::sinterstore_range, destination, first, last); + } + + template + QueuedRedis& sinterstore(const StringView &destination, std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + QueuedRedis& sismember(const StringView &key, const StringView &member) { + return command(cmd::sismember, key, member); + } + + QueuedRedis& smembers(const StringView &key) { + return command(cmd::smembers, key); + } + + QueuedRedis& smove(const StringView &source, + const StringView &destination, + const StringView &member) { + return command(cmd::smove, source, destination, member); + } + + QueuedRedis& spop(const StringView &key) { + return command(cmd::spop, key); + } + + QueuedRedis& spop(const StringView &key, long long count) { + return command(cmd::spop_range, key, count); + } + + QueuedRedis& srandmember(const StringView &key) { + return command(cmd::srandmember, key); + } + + QueuedRedis& srandmember(const StringView &key, long long count) { + return command(cmd::srandmember_range, key, count); + } + + QueuedRedis& srem(const StringView &key, const StringView &member) { + return command(cmd::srem, key, member); + } + + template + QueuedRedis& srem(const StringView &key, Input first, Input last) { + range_check("SREM", first, last); + + return command(cmd::srem_range, key, first, last); + } + + template + QueuedRedis& srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::sscan, key, cursor, pattern, count); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return sscan(key, cursor, pattern, 10); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + long long count) { + return sscan(key, cursor, "*", count); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor) { + return sscan(key, cursor, "*", 10); + } + + template + QueuedRedis& sunion(Input first, Input last) { + range_check("SUNION", first, last); + + return command(cmd::sunion, first, last); + } + + template + QueuedRedis& sunion(std::initializer_list il) { + return sunion(il.begin(), il.end()); + } + + QueuedRedis& sunionstore(const StringView &destination, const StringView &key) { + return command(cmd::sunionstore, destination, key); + } + + template + QueuedRedis& sunionstore(const StringView &destination, Input first, Input last) { + range_check("SUNIONSTORE", first, last); + + return command(cmd::sunionstore_range, destination, first, last); + } + + template + QueuedRedis& sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + QueuedRedis& bzpopmax(const StringView &key, long long timeout) { + return command(cmd::bzpopmax, key, timeout); + } + + QueuedRedis& bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(key, timeout.count()); + } + + template + QueuedRedis& bzpopmax(Input first, Input last, long long timeout) { + range_check("BZPOPMAX", first, last); + + return command(cmd::bzpopmax_range, first, last, timeout); + } + + template + QueuedRedis& bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(first, last, timeout.count()); + } + + template + QueuedRedis& bzpopmax(std::initializer_list il, long long timeout) { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(il.begin(), il.end(), timeout); + } + + QueuedRedis& bzpopmin(const StringView &key, long long timeout) { + return command(cmd::bzpopmin, key, timeout); + } + + QueuedRedis& bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(key, timeout.count()); + } + + template + QueuedRedis& bzpopmin(Input first, Input last, long long timeout) { + range_check("BZPOPMIN", first, last); + + return command(cmd::bzpopmin_range, first, last, timeout); + } + + template + QueuedRedis& bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(first, last, timeout.count()); + } + + template + QueuedRedis& bzpopmin(std::initializer_list il, long long timeout) { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + QueuedRedis& zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return command(cmd::zadd, key, member, score, type, changed); + } + + template + QueuedRedis& zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + range_check("ZADD", first, last); + + return command(cmd::zadd_range, key, first, last, type, changed); + } + + QueuedRedis& zcard(const StringView &key) { + return command(cmd::zcard, key); + } + + template + QueuedRedis& zcount(const StringView &key, const Interval &interval) { + return command(cmd::zcount, key, interval); + } + + QueuedRedis& zincrby(const StringView &key, double increment, const StringView &member) { + return command(cmd::zincrby, key, increment, member); + } + + QueuedRedis& zinterstore(const StringView &destination, + const StringView &key, + double weight) { + return command(cmd::zinterstore, destination, key, weight); + } + + template + QueuedRedis& zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM) { + range_check("ZINTERSTORE", first, last); + + return command(cmd::zinterstore_range, destination, first, last, type); + } + + template + QueuedRedis& zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + QueuedRedis& zlexcount(const StringView &key, const Interval &interval) { + return command(cmd::zlexcount, key, interval); + } + + QueuedRedis& zpopmax(const StringView &key) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::zpopmax, key, 1); + } + + QueuedRedis& zpopmax(const StringView &key, long long count) { + return command(cmd::zpopmax, key, count); + } + + QueuedRedis& zpopmin(const StringView &key) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::zpopmin, key, 1); + } + + QueuedRedis& zpopmin(const StringView &key, long long count) { + return command(cmd::zpopmin, key, count); + } + + // NOTE: *QueuedRedis::zrange*'s parameters are different from *Redis::zrange*. + // *Redis::zrange* is overloaded by the output iterator, however, there's no such + // iterator in *QueuedRedis::zrange*. So we have to use an extra parameter: *with_scores*, + // to decide whether we should send *WITHSCORES* option to Redis. This also applies to + // other commands with the *WITHSCORES* option, e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, + // *ZREVRANGEBYSCORE*. + QueuedRedis& zrange(const StringView &key, + long long start, + long long stop, + bool with_scores = false) { + return command(cmd::zrange, key, start, stop, with_scores); + } + + template + QueuedRedis& zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return command(cmd::zrangebylex, key, interval, opts); + } + + template + QueuedRedis& zrangebylex(const StringView &key, const Interval &interval) { + return zrangebylex(key, interval, {}); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores = false) { + return command(cmd::zrangebyscore, key, interval, opts, with_scores); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrangebyscore(const StringView &key, + const Interval &interval, + bool with_scores = false) { + return zrangebyscore(key, interval, {}, with_scores); + } + + QueuedRedis& zrank(const StringView &key, const StringView &member) { + return command(cmd::zrank, key, member); + } + + QueuedRedis& zrem(const StringView &key, const StringView &member) { + return command(cmd::zrem, key, member); + } + + template + QueuedRedis& zrem(const StringView &key, Input first, Input last) { + range_check("ZREM", first, last); + + return command(cmd::zrem_range, key, first, last); + } + + template + QueuedRedis& zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + QueuedRedis& zremrangebylex(const StringView &key, const Interval &interval) { + return command(cmd::zremrangebylex, key, interval); + } + + QueuedRedis& zremrangebyrank(const StringView &key, long long start, long long stop) { + return command(cmd::zremrangebyrank, key, start, stop); + } + + template + QueuedRedis& zremrangebyscore(const StringView &key, const Interval &interval) { + return command(cmd::zremrangebyscore, key, interval); + } + + // See comments on *ZRANGE*. + QueuedRedis& zrevrange(const StringView &key, + long long start, + long long stop, + bool with_scores = false) { + return command(cmd::zrevrange, key, start, stop, with_scores); + } + + template + QueuedRedis& zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return command(cmd::zrevrangebylex, key, interval, opts); + } + + template + QueuedRedis& zrevrangebylex(const StringView &key, const Interval &interval) { + return zrevrangebylex(key, interval, {}); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores = false) { + return command(cmd::zrevrangebyscore, key, interval, opts, with_scores); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrevrangebyscore(const StringView &key, + const Interval &interval, + bool with_scores = false) { + return zrevrangebyscore(key, interval, {}, with_scores); + } + + QueuedRedis& zrevrank(const StringView &key, const StringView &member) { + return command(cmd::zrevrank, key, member); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::zscan, key, cursor, pattern, count); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return zscan(key, cursor, pattern, 10); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + long long count) { + return zscan(key, cursor, "*", count); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor) { + return zscan(key, cursor, "*", 10); + } + + QueuedRedis& zscore(const StringView &key, const StringView &member) { + return command(cmd::zscore, key, member); + } + + QueuedRedis& zunionstore(const StringView &destination, + const StringView &key, + double weight) { + return command(cmd::zunionstore, destination, key, weight); + } + + template + QueuedRedis& zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM) { + range_check("ZUNIONSTORE", first, last); + + return command(cmd::zunionstore_range, destination, first, last, type); + } + + template + QueuedRedis& zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + QueuedRedis& pfadd(const StringView &key, const StringView &element) { + return command(cmd::pfadd, key, element); + } + + template + QueuedRedis& pfadd(const StringView &key, Input first, Input last) { + range_check("PFADD", first, last); + + return command(cmd::pfadd_range, key, first, last); + } + + template + QueuedRedis& pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + QueuedRedis& pfcount(const StringView &key) { + return command(cmd::pfcount, key); + } + + template + QueuedRedis& pfcount(Input first, Input last) { + range_check("PFCOUNT", first, last); + + return command(cmd::pfcount_range, first, last); + } + + template + QueuedRedis& pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + QueuedRedis& pfmerge(const StringView &destination, const StringView &key) { + return command(cmd::pfmerge, destination, key); + } + + template + QueuedRedis& pfmerge(const StringView &destination, Input first, Input last) { + range_check("PFMERGE", first, last); + + return command(cmd::pfmerge_range, destination, first, last); + } + + template + QueuedRedis& pfmerge(const StringView &destination, std::initializer_list il) { + return pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + QueuedRedis& geoadd(const StringView &key, + const std::tuple &member) { + return command(cmd::geoadd, key, member); + } + + template + QueuedRedis& geoadd(const StringView &key, + Input first, + Input last) { + range_check("GEOADD", first, last); + + return command(cmd::geoadd_range, key, first, last); + } + + template + QueuedRedis& geoadd(const StringView &key, std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + QueuedRedis& geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M) { + return command(cmd::geodist, key, member1, member2, unit); + } + + // Call reply::parse_leniently to parse the reply. + QueuedRedis& geohash(const StringView &key, const StringView &member) { + return command(cmd::geohash, key, member); + } + + template + QueuedRedis& geohash(const StringView &key, Input first, Input last) { + range_check("GEOHASH", first, last); + + return command(cmd::geohash_range, key, first, last); + } + + template + QueuedRedis& geohash(const StringView &key, std::initializer_list il) { + return geohash(key, il.begin(), il.end()); + } + + // Call reply::parse_leniently to parse the reply. + QueuedRedis& geopos(const StringView &key, const StringView &member) { + return command(cmd::geopos, key, member); + } + + template + QueuedRedis& geopos(const StringView &key, Input first, Input last) { + range_check("GEOPOS", first, last); + + return command(cmd::geopos_range, key, first, last); + } + + template + QueuedRedis& geopos(const StringView &key, std::initializer_list il) { + return geopos(key, il.begin(), il.end()); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + QueuedRedis& georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::georadius_store, + key, + loc, + radius, + unit, + destination, + store_dist, + count); + } + + // NOTE: *QueuedRedis::georadius*'s parameters are different from *Redis::georadius*. + // *Redis::georadius* is overloaded by the output iterator, however, there's no such + // iterator in *QueuedRedis::georadius*. So we have to use extra parameters to decide + // whether we should send options to Redis. This also applies to *GEORADIUSBYMEMBER*. + QueuedRedis& georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + return command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + } + + QueuedRedis& georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::georadiusbymember, + key, + member, + radius, + unit, + destination, + store_dist, + count); + } + + // See the comments on *GEORADIUS*. + QueuedRedis& georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + return command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + } + + // SCRIPTING commands. + + template + QueuedRedis& eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + return command(cmd::eval, script, keys_first, keys_last, args_first, args_last); + } + + QueuedRedis& eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return eval(script, keys.begin(), keys.end(), args.begin(), args.end()); + } + + template + QueuedRedis& evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + return command(cmd::evalsha, script, keys_first, keys_last, args_first, args_last); + } + + QueuedRedis& evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return evalsha(script, keys.begin(), keys.end(), args.begin(), args.end()); + } + + // Call reply::parse_leniently to parse the reply. + QueuedRedis& script_exists(const StringView &sha1) { + return command(cmd::script_exists, sha1); + } + + template + QueuedRedis& script_exists(Input first, Input last) { + range_check("SCRIPT EXISTS", first, last); + + return command(cmd::script_exists_range, first, last); + } + + template + QueuedRedis& script_exists(std::initializer_list il) { + return script_exists(il.begin(), il.end()); + } + + QueuedRedis& script_flush() { + return command(cmd::script_flush); + } + + QueuedRedis& script_kill() { + return command(cmd::script_kill); + } + + QueuedRedis& script_load(const StringView &script) { + return command(cmd::script_load, script); + } + + // PUBSUB commands. + + QueuedRedis& publish(const StringView &channel, const StringView &message) { + return command(cmd::publish, channel, message); + } + + // Stream commands. + + QueuedRedis& xack(const StringView &key, const StringView &group, const StringView &id) { + return command(cmd::xack, key, group, id); + } + + template + QueuedRedis& xack(const StringView &key, const StringView &group, Input first, Input last) { + range_check("XACK", first, last); + + return command(cmd::xack_range, key, group, first, last); + } + + template + QueuedRedis& xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + QueuedRedis& xadd(const StringView &key, const StringView &id, Input first, Input last) { + range_check("XADD", first, last); + + return command(cmd::xadd_range, key, id, first, last); + } + + template + QueuedRedis& xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + QueuedRedis& xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true) { + range_check("XADD", first, last); + + return command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + } + + template + QueuedRedis& xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id) { + return command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + } + + template + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last) { + range_check("XCLAIM", first, last); + + return command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + } + + template + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il) { + return xclaim(key, group, consumer, min_idle_time, il.begin(), il.end()); + } + + QueuedRedis& xdel(const StringView &key, const StringView &id) { + return command(cmd::xdel, key, id); + } + + template + QueuedRedis& xdel(const StringView &key, Input first, Input last) { + range_check("XDEL", first, last); + + return command(cmd::xdel_range, key, first, last); + } + + template + QueuedRedis& xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + QueuedRedis& xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false) { + return command(cmd::xgroup_create, key, group, id, mkstream); + } + + QueuedRedis& xgroup_setid(const StringView &key, + const StringView &group, + const StringView &id) { + return command(cmd::xgroup_setid, key, group, id); + } + + QueuedRedis& xgroup_destroy(const StringView &key, const StringView &group) { + return command(cmd::xgroup_destroy, key, group); + } + + QueuedRedis& xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer) { + return command(cmd::xgroup_delconsumer, key, group, consumer); + } + + QueuedRedis& xlen(const StringView &key) { + return command(cmd::xlen, key); + } + + QueuedRedis& xpending(const StringView &key, const StringView &group) { + return command(cmd::xpending, key, group); + } + + QueuedRedis& xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count) { + return command(cmd::xpending_detail, key, group, start, end, count); + } + + QueuedRedis& xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer) { + return command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + } + + QueuedRedis& xrange(const StringView &key, + const StringView &start, + const StringView &end) { + return command(cmd::xrange, key, start, end); + } + + QueuedRedis& xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count) { + return command(cmd::xrange_count, key, start, end, count); + } + + QueuedRedis& xread(const StringView &key, const StringView &id, long long count) { + return command(cmd::xread, key, id, count); + } + + QueuedRedis& xread(const StringView &key, const StringView &id) { + return xread(key, id, 0); + } + + template + auto xread(Input first, Input last, long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREAD", first, last); + + return command(cmd::xread_range, first, last, count); + } + + template + auto xread(Input first, Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xread(first, last, 0); + } + + QueuedRedis& xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count) { + return command(cmd::xread_block, key, id, timeout.count(), count); + } + + QueuedRedis& xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout) { + return xread(key, id, timeout, 0); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREAD", first, last); + + return command(cmd::xread_block_range, first, last, timeout.count(), count); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xread(first, last, timeout, 0); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack) { + return command(cmd::xreadgroup, group, consumer, key, id, count, noack); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count) { + return xreadgroup(group, consumer, key, id, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREADGROUP", first, last); + + return command(cmd::xreadgroup_range, group, consumer, first, last, count, noack); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first ,last, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first ,last, 0, false); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack) { + return command(cmd::xreadgroup_block, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count) { + return xreadgroup(group, consumer, key, id, timeout, count, false); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout) { + return xreadgroup(group, consumer, key, id, timeout, 0, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREADGROUP", first, last); + + return command(cmd::xreadgroup_block_range, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first, last, timeout, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first, last, timeout, 0, false); + } + + QueuedRedis& xrevrange(const StringView &key, + const StringView &end, + const StringView &start) { + return command(cmd::xrevrange, key, end, start); + } + + QueuedRedis& xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count) { + return command(cmd::xrevrange_count, key, end, start, count); + } + + QueuedRedis& xtrim(const StringView &key, long long count, bool approx = true) { + return command(cmd::xtrim, key, count, approx); + } + +private: + friend class Redis; + + friend class RedisCluster; + + template + QueuedRedis(const ConnectionPoolSPtr &pool, bool new_connection, Args &&...args); + + Connection& _connection(); + + void _sanity_check(); + + void _reset(bool reset_connection = true); + + void _return_connection(); + + void _invalidate(); + + void _clean_up(); + + void _rewrite_replies(std::vector &replies) const; + + template + void _rewrite_replies(const std::vector &indexes, + Func rewriter, + std::vector &replies) const; + + GuardedConnectionSPtr _guarded_connection; + + ConnectionPoolSPtr _connection_pool; + + bool _new_connection = true; + + Impl _impl; + + std::size_t _cmd_num = 0; + + std::vector _set_cmd_indexes; + + std::vector _empty_array_cmd_indexes; + + bool _valid = true; +}; + +class QueuedReplies { +public: + QueuedReplies() = default; + + QueuedReplies(const QueuedReplies &) = delete; + QueuedReplies& operator=(const QueuedReplies &) = delete; + + QueuedReplies(QueuedReplies &&) = default; + QueuedReplies& operator=(QueuedReplies &&) = default; + + ~QueuedReplies() = default; + + std::size_t size() const; + + redisReply& get(std::size_t idx); + + template + Result get(std::size_t idx); + + template + void get(std::size_t idx, Output output); + +private: + template + friend class QueuedRedis; + + explicit QueuedReplies(std::vector replies) : _replies(std::move(replies)) {} + + void _index_check(std::size_t idx) const; + + std::vector _replies; +}; + +} + +} + +#include "queued_redis.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/queued_redis.hpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/queued_redis.hpp new file mode 100644 index 000000000..d2865e46e --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/queued_redis.hpp @@ -0,0 +1,275 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP +#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP + +namespace sw { + +namespace redis { + +template +template +QueuedRedis::QueuedRedis(const ConnectionPoolSPtr &pool, + bool new_connection, + Args &&...args) : + _new_connection(new_connection), + _impl(std::forward(args)...) { + assert(pool); + + if (_new_connection) { + _connection_pool = std::make_shared(pool->clone()); + } else { + // Create a connection from the origin pool. + _connection_pool = pool; + } +} + +template +QueuedRedis::~QueuedRedis() { + try { + _clean_up(); + } catch (const Error &e) { + // Ensure the destructor does not throw + } +} + +template +Redis QueuedRedis::redis() { + _sanity_check(); + + assert(_guarded_connection); + + return Redis(_guarded_connection); +} + +template +template +auto QueuedRedis::command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, + QueuedRedis&>::type { + try { + _sanity_check(); + + _impl.command(_connection(), cmd, std::forward(args)...); + + ++_cmd_num; + } catch (const Error &e) { + _invalidate(); + throw; + } + + return *this; +} + +template +template +QueuedRedis& QueuedRedis::command(const StringView &cmd_name, Args &&...args) { + auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, std::forward(args)...); + connection.send(cmd_args); + }; + + return command(cmd, cmd_name, std::forward(args)...); +} + +template +template +auto QueuedRedis::command(Input first, Input last) + -> typename std::enable_if::value, QueuedRedis&>::type { + if (first == last) { + throw Error("command: empty range"); + } + + auto cmd = [](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +QueuedReplies QueuedRedis::exec() { + try { + _sanity_check(); + + auto replies = _impl.exec(_connection(), _cmd_num); + + _rewrite_replies(replies); + + _reset(); + + return QueuedReplies(std::move(replies)); + } catch (const WatchError &e) { + // In this case, we only clear some states and keep the connection, + // so that user can retry the transaction. + _reset(false); + throw; + } catch (const Error &e) { + _invalidate(); + throw; + } +} + +template +void QueuedRedis::discard() { + try { + _sanity_check(); + + _impl.discard(_connection(), _cmd_num); + + _reset(); + } catch (const Error &e) { + _invalidate(); + throw; + } +} + +template +Connection& QueuedRedis::_connection() { + assert(_valid); + + if (!_guarded_connection) { + _guarded_connection = std::make_shared(_connection_pool); + } + + return _guarded_connection->connection(); +} + +template +void QueuedRedis::_sanity_check() { + if (!_valid) { + throw Error("Not in valid state"); + } + + if (_connection().broken()) { + throw Error("Connection is broken"); + } +} + +template +inline void QueuedRedis::_reset(bool reset_connection) { + if (reset_connection && !_new_connection) { + _return_connection(); + } + + _cmd_num = 0; + + _set_cmd_indexes.clear(); + + _empty_array_cmd_indexes.clear(); +} + +template +inline void QueuedRedis::_return_connection() { + if (_guarded_connection.use_count() == 1) { + // If no one else holding the connection, return it back to pool. + // Instead, if some other `Redis` object holds the connection, + // e.g. `auto redis = transaction.redis();`, we cannot return the connection. + _guarded_connection.reset(); + } +} + +template +void QueuedRedis::_invalidate() { + _valid = false; + + _clean_up(); + + _reset(); +} + +template +void QueuedRedis::_clean_up() { + if (_guarded_connection && !_new_connection) { + // Something bad happened, we need to close the current connection + // before returning it back to pool. + _guarded_connection->connection().invalidate(); + } +} + +template +void QueuedRedis::_rewrite_replies(std::vector &replies) const { + _rewrite_replies(_set_cmd_indexes, reply::rewrite_set_reply, replies); + + _rewrite_replies(_empty_array_cmd_indexes, reply::rewrite_empty_array_reply, replies); +} + +template +template +void QueuedRedis::_rewrite_replies(const std::vector &indexes, + Func rewriter, + std::vector &replies) const { + for (auto idx : indexes) { + assert(idx < replies.size()); + + auto &reply = replies[idx]; + + assert(reply); + + rewriter(*reply); + } +} + +inline std::size_t QueuedReplies::size() const { + return _replies.size(); +} + +inline redisReply& QueuedReplies::get(std::size_t idx) { + _index_check(idx); + + auto &reply = _replies[idx]; + + assert(reply); + + if (reply::is_error(*reply)) { + throw_error(*reply); + } + + return *reply; +} + +template +inline Result QueuedReplies::get(std::size_t idx) { + auto &reply = get(idx); + + return reply::parse(reply); +} + +template +inline void QueuedReplies::get(std::size_t idx, Output output) { + auto &reply = get(idx); + + reply::to_array(reply, output); +} + +inline void QueuedReplies::_index_check(std::size_t idx) const { + if (idx >= size()) { + throw Error("Out of range"); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis++.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis++.h new file mode 100644 index 000000000..0da0ebb16 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis++.h @@ -0,0 +1,25 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H +#define SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H + +#include "redis.h" +#include "redis_cluster.h" +#include "queued_redis.h" +#include "sentinel.h" + +#endif // end SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.cpp new file mode 100644 index 000000000..e47de333f --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.cpp @@ -0,0 +1,929 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "redis.h" +#include +#include "command.h" +#include "errors.h" +#include "queued_redis.h" + +namespace sw { + +namespace redis { + +Redis::Redis(const std::string &uri) : Redis(ConnectionOptions(uri)) {} + +Redis::Redis(const GuardedConnectionSPtr &connection) : _connection(connection) { + assert(_connection); +} + +Pipeline Redis::pipeline(bool new_connection) { + if (!_pool) { + throw Error("cannot create pipeline in single connection mode"); + } + + return Pipeline(_pool, new_connection); +} + +Transaction Redis::transaction(bool piped, bool new_connection) { + if (!_pool) { + throw Error("cannot create transaction in single connection mode"); + } + + return Transaction(_pool, new_connection, piped); +} + +Subscriber Redis::subscriber() { + if (!_pool) { + throw Error("cannot create subscriber in single connection mode"); + } + + return Subscriber(_pool->create()); +} + +// CONNECTION commands. + +void Redis::auth(const StringView &password) { + auto reply = command(cmd::auth, password); + + reply::parse(*reply); +} + +void Redis::auth(const StringView &user, const StringView &password) { + auto reply = command( + cmd::auth, user, password); + + reply::parse(*reply); +} + +std::string Redis::echo(const StringView &msg) { + auto reply = command(cmd::echo, msg); + + return reply::parse(*reply); +} + +std::string Redis::ping() { + auto reply = command(cmd::ping); + + return reply::to_status(*reply); +} + +std::string Redis::ping(const StringView &msg) { + auto reply = command(cmd::ping, msg); + + return reply::parse(*reply); +} + +void Redis::swapdb(long long idx1, long long idx2) { + auto reply = command(cmd::swapdb, idx1, idx2); + + reply::parse(*reply); +} + +// SERVER commands. + +void Redis::bgrewriteaof() { + auto reply = command(cmd::bgrewriteaof); + + reply::parse(*reply); +} + +void Redis::bgsave() { + auto reply = command(cmd::bgsave); + + reply::parse(*reply); +} + +long long Redis::dbsize() { + auto reply = command(cmd::dbsize); + + return reply::parse(*reply); +} + +void Redis::flushall(bool async) { + auto reply = command(cmd::flushall, async); + + reply::parse(*reply); +} + +void Redis::flushdb(bool async) { + auto reply = command(cmd::flushdb, async); + + reply::parse(*reply); +} + +std::string Redis::info() { + auto reply = command(cmd::info); + + return reply::parse(*reply); +} + +std::string Redis::info(const StringView §ion) { + auto reply = command(cmd::info, section); + + return reply::parse(*reply); +} + +long long Redis::lastsave() { + auto reply = command(cmd::lastsave); + + return reply::parse(*reply); +} + +void Redis::save() { + auto reply = command(cmd::save); + + reply::parse(*reply); +} + +// KEY commands. + +long long Redis::del(const StringView &key) { + auto reply = command(cmd::del, key); + + return reply::parse(*reply); +} + +OptionalString Redis::dump(const StringView &key) { + auto reply = command(cmd::dump, key); + + return reply::parse(*reply); +} + +long long Redis::exists(const StringView &key) { + auto reply = command(cmd::exists, key); + + return reply::parse(*reply); +} + +bool Redis::expire(const StringView &key, long long timeout) { + auto reply = command(cmd::expire, key, timeout); + + return reply::parse(*reply); +} + +bool Redis::expireat(const StringView &key, long long timestamp) { + auto reply = command(cmd::expireat, key, timestamp); + + return reply::parse(*reply); +} + +bool Redis::move(const StringView &key, long long db) { + auto reply = command(cmd::move, key, db); + + return reply::parse(*reply); +} + +bool Redis::persist(const StringView &key) { + auto reply = command(cmd::persist, key); + + return reply::parse(*reply); +} + +bool Redis::pexpire(const StringView &key, long long timeout) { + auto reply = command(cmd::pexpire, key, timeout); + + return reply::parse(*reply); +} + +bool Redis::pexpireat(const StringView &key, long long timestamp) { + auto reply = command(cmd::pexpireat, key, timestamp); + + return reply::parse(*reply); +} + +long long Redis::pttl(const StringView &key) { + auto reply = command(cmd::pttl, key); + + return reply::parse(*reply); +} + +OptionalString Redis::randomkey() { + auto reply = command(cmd::randomkey); + + return reply::parse(*reply); +} + +void Redis::rename(const StringView &key, const StringView &newkey) { + auto reply = command(cmd::rename, key, newkey); + + reply::parse(*reply); +} + +bool Redis::renamenx(const StringView &key, const StringView &newkey) { + auto reply = command(cmd::renamenx, key, newkey); + + return reply::parse(*reply); +} + +void Redis::restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace) { + auto reply = command(cmd::restore, key, val, ttl, replace); + + reply::parse(*reply); +} + +long long Redis::touch(const StringView &key) { + auto reply = command(cmd::touch, key); + + return reply::parse(*reply); +} + +long long Redis::ttl(const StringView &key) { + auto reply = command(cmd::ttl, key); + + return reply::parse(*reply); +} + +std::string Redis::type(const StringView &key) { + auto reply = command(cmd::type, key); + + return reply::parse(*reply); +} + +long long Redis::unlink(const StringView &key) { + auto reply = command(cmd::unlink, key); + + return reply::parse(*reply); +} + +long long Redis::wait(long long numslaves, long long timeout) { + auto reply = command(cmd::wait, numslaves, timeout); + + return reply::parse(*reply); +} + +// STRING commands. + +long long Redis::append(const StringView &key, const StringView &val) { + auto reply = command(cmd::append, key, val); + + return reply::parse(*reply); +} + +long long Redis::bitcount(const StringView &key, long long start, long long end) { + auto reply = command(cmd::bitcount, key, start, end); + + return reply::parse(*reply); +} + +long long Redis::bitop(BitOp op, const StringView &destination, const StringView &key) { + auto reply = command(cmd::bitop, op, destination, key); + + return reply::parse(*reply); +} + +long long Redis::bitpos(const StringView &key, + long long bit, + long long start, + long long end) { + auto reply = command(cmd::bitpos, key, bit, start, end); + + return reply::parse(*reply); +} + +long long Redis::decr(const StringView &key) { + auto reply = command(cmd::decr, key); + + return reply::parse(*reply); +} + +long long Redis::decrby(const StringView &key, long long decrement) { + auto reply = command(cmd::decrby, key, decrement); + + return reply::parse(*reply); +} + +OptionalString Redis::get(const StringView &key) { + auto reply = command(cmd::get, key); + + return reply::parse(*reply); +} + +long long Redis::getbit(const StringView &key, long long offset) { + auto reply = command(cmd::getbit, key, offset); + + return reply::parse(*reply); +} + +std::string Redis::getrange(const StringView &key, long long start, long long end) { + auto reply = command(cmd::getrange, key, start, end); + + return reply::parse(*reply); +} + +OptionalString Redis::getset(const StringView &key, const StringView &val) { + auto reply = command(cmd::getset, key, val); + + return reply::parse(*reply); +} + +long long Redis::incr(const StringView &key) { + auto reply = command(cmd::incr, key); + + return reply::parse(*reply); +} + +long long Redis::incrby(const StringView &key, long long increment) { + auto reply = command(cmd::incrby, key, increment); + + return reply::parse(*reply); +} + +double Redis::incrbyfloat(const StringView &key, double increment) { + auto reply = command(cmd::incrbyfloat, key, increment); + + return reply::parse(*reply); +} + +void Redis::psetex(const StringView &key, + long long ttl, + const StringView &val) { + auto reply = command(cmd::psetex, key, ttl, val); + + reply::parse(*reply); +} + +bool Redis::set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + UpdateType type) { + auto reply = command(cmd::set, key, val, ttl.count(), type); + + reply::rewrite_set_reply(*reply); + + return reply::parse(*reply); +} + +void Redis::setex(const StringView &key, + long long ttl, + const StringView &val) { + auto reply = command(cmd::setex, key, ttl, val); + + reply::parse(*reply); +} + +bool Redis::setnx(const StringView &key, const StringView &val) { + auto reply = command(cmd::setnx, key, val); + + return reply::parse(*reply); +} + +long long Redis::setrange(const StringView &key, long long offset, const StringView &val) { + auto reply = command(cmd::setrange, key, offset, val); + + return reply::parse(*reply); +} + +long long Redis::strlen(const StringView &key) { + auto reply = command(cmd::strlen, key); + + return reply::parse(*reply); +} + +// LIST commands. + +OptionalStringPair Redis::blpop(const StringView &key, long long timeout) { + auto reply = command(cmd::blpop, key, timeout); + + return reply::parse(*reply); +} + +OptionalStringPair Redis::blpop(const StringView &key, const std::chrono::seconds &timeout) { + return blpop(key, timeout.count()); +} + +OptionalStringPair Redis::brpop(const StringView &key, long long timeout) { + auto reply = command(cmd::brpop, key, timeout); + + return reply::parse(*reply); +} + +OptionalStringPair Redis::brpop(const StringView &key, const std::chrono::seconds &timeout) { + return brpop(key, timeout.count()); +} + +OptionalString Redis::brpoplpush(const StringView &source, + const StringView &destination, + long long timeout) { + auto reply = command(cmd::brpoplpush, source, destination, timeout); + + return reply::parse(*reply); +} + +OptionalString Redis::lindex(const StringView &key, long long index) { + auto reply = command(cmd::lindex, key, index); + + return reply::parse(*reply); +} + +long long Redis::linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val) { + auto reply = command(cmd::linsert, key, position, pivot, val); + + return reply::parse(*reply); +} + +long long Redis::llen(const StringView &key) { + auto reply = command(cmd::llen, key); + + return reply::parse(*reply); +} + +OptionalString Redis::lpop(const StringView &key) { + auto reply = command(cmd::lpop, key); + + return reply::parse(*reply); +} + +long long Redis::lpush(const StringView &key, const StringView &val) { + auto reply = command(cmd::lpush, key, val); + + return reply::parse(*reply); +} + +long long Redis::lpushx(const StringView &key, const StringView &val) { + auto reply = command(cmd::lpushx, key, val); + + return reply::parse(*reply); +} + +long long Redis::lrem(const StringView &key, long long count, const StringView &val) { + auto reply = command(cmd::lrem, key, count, val); + + return reply::parse(*reply); +} + +void Redis::lset(const StringView &key, long long index, const StringView &val) { + auto reply = command(cmd::lset, key, index, val); + + reply::parse(*reply); +} + +void Redis::ltrim(const StringView &key, long long start, long long stop) { + auto reply = command(cmd::ltrim, key, start, stop); + + reply::parse(*reply); +} + +OptionalString Redis::rpop(const StringView &key) { + auto reply = command(cmd::rpop, key); + + return reply::parse(*reply); +} + +OptionalString Redis::rpoplpush(const StringView &source, const StringView &destination) { + auto reply = command(cmd::rpoplpush, source, destination); + + return reply::parse(*reply); +} + +long long Redis::rpush(const StringView &key, const StringView &val) { + auto reply = command(cmd::rpush, key, val); + + return reply::parse(*reply); +} + +long long Redis::rpushx(const StringView &key, const StringView &val) { + auto reply = command(cmd::rpushx, key, val); + + return reply::parse(*reply); +} + +long long Redis::hdel(const StringView &key, const StringView &field) { + auto reply = command(cmd::hdel, key, field); + + return reply::parse(*reply); +} + +bool Redis::hexists(const StringView &key, const StringView &field) { + auto reply = command(cmd::hexists, key, field); + + return reply::parse(*reply); +} + +OptionalString Redis::hget(const StringView &key, const StringView &field) { + auto reply = command(cmd::hget, key, field); + + return reply::parse(*reply); +} + +long long Redis::hincrby(const StringView &key, const StringView &field, long long increment) { + auto reply = command(cmd::hincrby, key, field, increment); + + return reply::parse(*reply); +} + +double Redis::hincrbyfloat(const StringView &key, const StringView &field, double increment) { + auto reply = command(cmd::hincrbyfloat, key, field, increment); + + return reply::parse(*reply); +} + +long long Redis::hlen(const StringView &key) { + auto reply = command(cmd::hlen, key); + + return reply::parse(*reply); +} + +bool Redis::hset(const StringView &key, const StringView &field, const StringView &val) { + auto reply = command(cmd::hset, key, field, val); + + return reply::parse(*reply); +} + +bool Redis::hset(const StringView &key, const std::pair &item) { + return hset(key, item.first, item.second); +} + +bool Redis::hsetnx(const StringView &key, const StringView &field, const StringView &val) { + auto reply = command(cmd::hsetnx, key, field, val); + + return reply::parse(*reply); +} + +bool Redis::hsetnx(const StringView &key, const std::pair &item) { + return hsetnx(key, item.first, item.second); +} + +long long Redis::hstrlen(const StringView &key, const StringView &field) { + auto reply = command(cmd::hstrlen, key, field); + + return reply::parse(*reply); +} + +// SET commands. + +long long Redis::sadd(const StringView &key, const StringView &member) { + auto reply = command(cmd::sadd, key, member); + + return reply::parse(*reply); +} + +long long Redis::scard(const StringView &key) { + auto reply = command(cmd::scard, key); + + return reply::parse(*reply); +} + +long long Redis::sdiffstore(const StringView &destination, const StringView &key) { + auto reply = command(cmd::sdiffstore, destination, key); + + return reply::parse(*reply); +} + +long long Redis::sinterstore(const StringView &destination, const StringView &key) { + auto reply = command(cmd::sinterstore, destination, key); + + return reply::parse(*reply); +} + +bool Redis::sismember(const StringView &key, const StringView &member) { + auto reply = command(cmd::sismember, key, member); + + return reply::parse(*reply); +} + +bool Redis::smove(const StringView &source, + const StringView &destination, + const StringView &member) { + auto reply = command(cmd::smove, source, destination, member); + + return reply::parse(*reply); +} + +OptionalString Redis::spop(const StringView &key) { + auto reply = command(cmd::spop, key); + + return reply::parse(*reply); +} + +OptionalString Redis::srandmember(const StringView &key) { + auto reply = command(cmd::srandmember, key); + + return reply::parse(*reply); +} + +long long Redis::srem(const StringView &key, const StringView &member) { + auto reply = command(cmd::srem, key, member); + + return reply::parse(*reply); +} + +long long Redis::sunionstore(const StringView &destination, const StringView &key) { + auto reply = command(cmd::sunionstore, destination, key); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +auto Redis::bzpopmax(const StringView &key, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax, key, timeout); + + return reply::parse>>(*reply); +} + +auto Redis::bzpopmin(const StringView &key, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin, key, timeout); + + return reply::parse>>(*reply); +} + +long long Redis::zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type, + bool changed) { + auto reply = command(cmd::zadd, key, member, score, type, changed); + + return reply::parse(*reply); +} + +long long Redis::zcard(const StringView &key) { + auto reply = command(cmd::zcard, key); + + return reply::parse(*reply); +} + +double Redis::zincrby(const StringView &key, double increment, const StringView &member) { + auto reply = command(cmd::zincrby, key, increment, member); + + return reply::parse(*reply); +} + +long long Redis::zinterstore(const StringView &destination, const StringView &key, double weight) { + auto reply = command(cmd::zinterstore, destination, key, weight); + + return reply::parse(*reply); +} + +Optional> Redis::zpopmax(const StringView &key) { + auto reply = command(cmd::zpopmax, key, 1); + + reply::rewrite_empty_array_reply(*reply); + + return reply::parse>>(*reply); +} + +Optional> Redis::zpopmin(const StringView &key) { + auto reply = command(cmd::zpopmin, key, 1); + + reply::rewrite_empty_array_reply(*reply); + + return reply::parse>>(*reply); +} + +OptionalLongLong Redis::zrank(const StringView &key, const StringView &member) { + auto reply = command(cmd::zrank, key, member); + + return reply::parse(*reply); +} + +long long Redis::zrem(const StringView &key, const StringView &member) { + auto reply = command(cmd::zrem, key, member); + + return reply::parse(*reply); +} + +long long Redis::zremrangebyrank(const StringView &key, long long start, long long stop) { + auto reply = command(cmd::zremrangebyrank, key, start, stop); + + return reply::parse(*reply); +} + +OptionalLongLong Redis::zrevrank(const StringView &key, const StringView &member) { + auto reply = command(cmd::zrevrank, key, member); + + return reply::parse(*reply); +} + +OptionalDouble Redis::zscore(const StringView &key, const StringView &member) { + auto reply = command(cmd::zscore, key, member); + + return reply::parse(*reply); +} + +long long Redis::zunionstore(const StringView &destination, const StringView &key, double weight) { + auto reply = command(cmd::zunionstore, destination, key, weight); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +bool Redis::pfadd(const StringView &key, const StringView &element) { + auto reply = command(cmd::pfadd, key, element); + + return reply::parse(*reply); +} + +long long Redis::pfcount(const StringView &key) { + auto reply = command(cmd::pfcount, key); + + return reply::parse(*reply); +} + +void Redis::pfmerge(const StringView &destination, const StringView &key) { + auto reply = command(cmd::pfmerge, destination, key); + + reply::parse(*reply); +} + +// GEO commands. + +long long Redis::geoadd(const StringView &key, + const std::tuple &member) { + auto reply = command(cmd::geoadd, key, member); + + return reply::parse(*reply); +} + +OptionalDouble Redis::geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit) { + auto reply = command(cmd::geodist, key, member1, member2, unit); + + return reply::parse(*reply); +} + +OptionalString Redis::geohash(const StringView &key, const StringView &member) { + auto reply = command(cmd::geohash, key, member); + + return reply::parse_leniently(*reply); +} + +Optional> Redis::geopos(const StringView &key, const StringView &member) { + auto reply = command(cmd::geopos, key, member); + + return reply::parse_leniently>>(*reply); +} + +OptionalLongLong Redis::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + auto reply = command(cmd::georadius_store, + key, + loc, + radius, + unit, + destination, + store_dist, + count); + + reply::rewrite_empty_array_reply(*reply); + + return reply::parse(*reply); +} + +OptionalLongLong Redis::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + auto reply = command(cmd::georadiusbymember_store, + key, + member, + radius, + unit, + destination, + store_dist, + count); + + reply::rewrite_empty_array_reply(*reply); + + return reply::parse(*reply); +} + +// SCRIPTING commands. + +bool Redis::script_exists(const StringView &sha1) { + auto reply = command(cmd::script_exists, sha1); + + return reply::parse_leniently(*reply); +} + +void Redis::script_flush() { + auto reply = command(cmd::script_flush); + + reply::parse(*reply); +} + +void Redis::script_kill() { + auto reply = command(cmd::script_kill); + + reply::parse(*reply); +} + +std::string Redis::script_load(const StringView &script) { + auto reply = command(cmd::script_load, script); + + return reply::parse(*reply); +} + +// PUBSUB commands. + +long long Redis::publish(const StringView &channel, const StringView &message) { + auto reply = command(cmd::publish, channel, message); + + return reply::parse(*reply); +} + +// Transaction commands. + +void Redis::watch(const StringView &key) { + auto reply = command(cmd::watch, key); + + reply::parse(*reply); +} + +void Redis::unwatch() { + auto reply = command(cmd::unwatch); + + reply::parse(*reply); +} + +// Stream commands. + +long long Redis::xack(const StringView &key, const StringView &group, const StringView &id) { + auto reply = command(cmd::xack, key, group, id); + + return reply::parse(*reply); +} + +long long Redis::xdel(const StringView &key, const StringView &id) { + auto reply = command(cmd::xdel, key, id); + + return reply::parse(*reply); +} + +void Redis::xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream) { + auto reply = command(cmd::xgroup_create, key, group, id, mkstream); + + reply::parse(*reply); +} + +void Redis::xgroup_setid(const StringView &key, const StringView &group, const StringView &id) { + auto reply = command(cmd::xgroup_setid, key, group, id); + + reply::parse(*reply); +} + +long long Redis::xgroup_destroy(const StringView &key, const StringView &group) { + auto reply = command(cmd::xgroup_destroy, key, group); + + return reply::parse(*reply); +} + +long long Redis::xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer) { + auto reply = command(cmd::xgroup_delconsumer, key, group, consumer); + + return reply::parse(*reply); +} + +long long Redis::xlen(const StringView &key) { + auto reply = command(cmd::xlen, key); + + return reply::parse(*reply); +} + +long long Redis::xtrim(const StringView &key, long long count, bool approx) { + auto reply = command(cmd::xtrim, key, count, approx); + + return reply::parse(*reply); +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.h new file mode 100644 index 000000000..1333e6b68 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.h @@ -0,0 +1,3631 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_H +#define SEWENEW_REDISPLUSPLUS_REDIS_H + +#include +#include +#include +#include +#include +#include "connection_pool.h" +#include "reply.h" +#include "command_options.h" +#include "utils.h" +#include "subscriber.h" +#include "pipeline.h" +#include "transaction.h" +#include "sentinel.h" + +namespace sw { + +namespace redis { + +template +class QueuedRedis; + +using Transaction = QueuedRedis; + +using Pipeline = QueuedRedis; + +class Redis { +public: + /// @brief Construct `Redis` instance with connection options and connection pool options. + /// @param connection_opts Connection options. + /// @param pool_opts Connection pool options. + /// @see `ConnectionOptions` + /// @see `ConnectionPoolOptions` + /// @see https://github.com/sewenew/redis-plus-plus#connection + explicit Redis(const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : + _pool(std::make_shared(pool_opts, connection_opts)) {} + + /// @brief Construct `Redis` instance with URI. + /// @param uri URI, e.g. 'tcp://127.0.0.1', 'tcp://127.0.0.1:6379', or 'unix://path/to/socket'. + /// Full URI scheme: 'tcp://[[username:]password@]host[:port][/db]' or + /// unix://[[username:]password@]path-to-unix-domain-socket[/db] + /// @see https://github.com/sewenew/redis-plus-plus/issues/37 + /// @see https://github.com/sewenew/redis-plus-plus#connection + explicit Redis(const std::string &uri); + + /// @brief Construct `Redis` instance with Redis sentinel, i.e. get node info from sentinel. + /// @param sentinel `Sentinel` instance. + /// @param master_name Name of master node. + /// @param role Connect to master node or slave node. + /// - Role::MASTER: Connect to master node. + /// - Role::SLAVE: Connect to slave node. + /// @param connection_opts Connection options. + /// @param pool_opts Connection pool options. + /// @see `Sentinel` + /// @see `Role` + /// @see https://github.com/sewenew/redis-plus-plus#redis-sentinel + Redis(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role, + const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : + _pool(std::make_shared(SimpleSentinel(sentinel, master_name, role), + pool_opts, + connection_opts)) {} + + /// @brief `Redis` is not copyable. + Redis(const Redis &) = delete; + + /// @brief `Redis` is not copyable. + Redis& operator=(const Redis &) = delete; + + /// @brief `Redis` is movable. + Redis(Redis &&) = default; + + /// @brief `Redis` is movable. + Redis& operator=(Redis &&) = default; + + /// @brief Create a pipeline. + /// @param new_connection Whether creating a `Pipeline` object in a new connection. + /// @return The created pipeline. + /// @note Instead of picking a connection from the underlying connection pool, + /// this method will create a new connection to Redis. So it's not a cheap operation, + /// and you'd better reuse the returned object as much as possible. + /// @see https://github.com/sewenew/redis-plus-plus#pipeline + Pipeline pipeline(bool new_connection = true); + + /// @brief Create a transaction. + /// @param piped Whether commands in a transaction should be sent in a pipeline to reduce RTT. + /// @param new_connection Whether creating a `Pipeline` object in a new connection. + /// @return The created transaction. + /// @note Instead of picking a connection from the underlying connection pool, + /// this method will create a new connection to Redis. So it's not a cheap operation, + /// and you'd better reuse the returned object as much as possible. + /// @see https://github.com/sewenew/redis-plus-plus#transaction + Transaction transaction(bool piped = false, bool new_connection = true); + + /// @brief Create a subscriber. + /// @return The created subscriber. + /// @note Instead of picking a connection from the underlying connection pool, + /// this method will create a new connection to Redis. So it's not a cheap operation, + /// and you'd better reuse the returned object as much as possible. + /// @see https://github.com/sewenew/redis-plus-plus#publishsubscribe + Subscriber subscriber(); + + template + auto command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, void>::type; + + template + Result command(const StringView &cmd_name, Args &&...args); + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Result>::type; + + template + auto command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type; + + // CONNECTION commands. + + /// @brief Send password to Redis. + /// @param password Password. + /// @note Normally, you should not call this method. + /// Instead, you should set password with `ConnectionOptions` or URI. + /// @see https://redis.io/commands/auth + void auth(const StringView &password); + + /// @brief Send user and password to Redis. + /// @param user User name. + /// @param password Password. + /// @note Normally, you should not call this method. + /// Instead, you should set password with `ConnectionOptions` or URI. + /// Also this overload only works with Redis 6.0 or later. + /// @see https://redis.io/commands/auth + void auth(const StringView &user, const StringView &password); + + /// @brief Ask Redis to return the given message. + /// @param msg Message to be sent. + /// @return Return the given message. + /// @see https://redis.io/commands/echo + std::string echo(const StringView &msg); + + /// @brief Test if the connection is alive. + /// @return Always return *PONG*. + /// @see https://redis.io/commands/ping + std::string ping(); + + /// @brief Test if the connection is alive. + /// @param msg Message sent to Redis. + /// @return Return the given message. + /// @see https://redis.io/commands/ping + std::string ping(const StringView &msg); + + // After sending QUIT, only the current connection will be close, while + // other connections in the pool is still open. This is a strange behavior. + // So we DO NOT support the QUIT command. If you want to quit connection to + // server, just destroy the Redis object. + // + // void quit(); + + // We get a connection from the pool, and send the SELECT command to switch + // to a specified DB. However, when we try to send other commands to the + // given DB, we might get a different connection from the pool, and these + // commands, in fact, work on other DB. e.g. + // + // redis.select(1); // get a connection from the pool and switch to the 1th DB + // redis.get("key"); // might get another connection from the pool, + // // and try to get 'key' on the default DB + // + // Obviously, this is NOT what we expect. So we DO NOT support SELECT command. + // In order to select a DB, we can specify the DB index with the ConnectionOptions. + // + // However, since Pipeline and Transaction always send multiple commands on a + // single connection, these two classes have a *select* method. + // + // void select(long long idx); + + /// @brief Swap two Redis databases. + /// @param idx1 The index of the first database. + /// @param idx2 The index of the second database. + /// @see https://redis.io/commands/swapdb + void swapdb(long long idx1, long long idx2); + + // SERVER commands. + + /// @brief Rewrite AOF in the background. + /// @see https://redis.io/commands/bgrewriteaof + void bgrewriteaof(); + + /// @brief Save database in the background. + /// @see https://redis.io/commands/bgsave + void bgsave(); + + /// @brief Get the size of the currently selected database. + /// @return Number of keys in currently selected database. + /// @see https://redis.io/commands/dbsize + long long dbsize(); + + /// @brief Remove keys of all databases. + /// @param async Whether flushing databases asynchronously, i.e. without blocking the server. + /// @see https://redis.io/commands/flushall + void flushall(bool async = false); + + /// @brief Remove keys of current databases. + /// @param async Whether flushing databases asynchronously, i.e. without blocking the server. + /// @see https://redis.io/commands/flushdb + void flushdb(bool async = false); + + /// @brief Get the info about the server. + /// @return Server info. + /// @see https://redis.io/commands/info + std::string info(); + + /// @brief Get the info about the server on the given section. + /// @param section Section. + /// @return Server info. + /// @see https://redis.io/commands/info + std::string info(const StringView §ion); + + /// @brief Get the UNIX timestamp in seconds, at which the database was saved successfully. + /// @return The last saving time. + /// @see https://redis.io/commands/lastsave + long long lastsave(); + + /// @brief Save databases into RDB file **synchronously**, i.e. block the server during saving. + /// @see https://redis.io/commands/save + void save(); + + // KEY commands. + + /// @brief Delete the given key. + /// @param key Key. + /// @return Number of keys removed. + /// @retval 1 If the key exists, and has been removed. + /// @retval 0 If the key does not exist. + /// @see https://redis.io/commands/del + long long del(const StringView &key); + + /// @brief Delete the given list of keys. + /// @param first Iterator to the first key in the range. + /// @param last Off-the-end iterator to the given range. + /// @return Number of keys removed. + /// @see https://redis.io/commands/del + template + long long del(Input first, Input last); + + /// @brief Delete the given list of keys. + /// @param il Initializer list of keys to be deleted. + /// @return Number of keys removed. + /// @see https://redis.io/commands/del + template + long long del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + /// @brief Get the serialized valued stored at key. + /// @param key Key. + /// @return The serialized value. + /// @note If key does not exist, `dump` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/dump + OptionalString dump(const StringView &key); + + /// @brief Check if the given key exists. + /// @param key Key. + /// @return Whether the given key exists. + /// @retval 1 If key exists. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/exists + long long exists(const StringView &key); + + /// @brief Check if the given keys exist. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the given range. + /// @return Number of keys existing. + /// @see https://redis.io/commands/exists + template + long long exists(Input first, Input last); + + /// @brief Check if the given keys exist. + /// @param il Initializer list of keys to be checked. + /// @return Number of keys existing. + /// @see https://redis.io/commands/exists + template + long long exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in seconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expire + bool expire(const StringView &key, long long timeout); + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in seconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expire + bool expire(const StringView &key, const std::chrono::seconds &timeout); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in seconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expireat + bool expireat(const StringView &key, long long timestamp); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in seconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expireat + bool expireat(const StringView &key, + const std::chrono::time_point &tp); + + /// @brief Get keys matching the given pattern. + /// @param pattern Pattern. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @note It's always a bad idea to call `keys`, since it might block Redis for a long time, + /// especially when the data set is very big. + /// @see `Redis::scan` + /// @see https://redis.io/commands/keys + template + void keys(const StringView &pattern, Output output); + + /// @brief Move a key to the given database. + /// @param key Key. + /// @param db The destination database. + /// @return Whether key has been moved. + /// @retval true If key has been moved. + /// @retval false If key was not moved. + /// @see https://redis.io/commands/move + bool move(const StringView &key, long long db); + + /// @brief Remove timeout on key. + /// @param key Key. + /// @return Whether timeout has been removed. + /// @retval true If timeout has been removed. + /// @retval false If key does not exist, or does not have an associated timeout. + /// @see https://redis.io/commands/persist + bool persist(const StringView &key); + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in milliseconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpire + bool pexpire(const StringView &key, long long timeout); + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in milliseconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpire + bool pexpire(const StringView &key, const std::chrono::milliseconds &timeout); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in milliseconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpireat + bool pexpireat(const StringView &key, long long timestamp); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in milliseconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpireat + bool pexpireat(const StringView &key, + const std::chrono::time_point &tp); + + /// @brief Get the TTL of a key in milliseconds. + /// @param key Key. + /// @return TTL of the key in milliseconds. + /// @see https://redis.io/commands/pttl + long long pttl(const StringView &key); + + /// @brief Get a random key from current database. + /// @return A random key. + /// @note If the database is empty, `randomkey` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/randomkey + OptionalString randomkey(); + + /// @brief Rename `key` to `newkey`. + /// @param key Key to be renamed. + /// @param newkey The new name of the key. + /// @see https://redis.io/commands/rename + void rename(const StringView &key, const StringView &newkey); + + /// @brief Rename `key` to `newkey` if `newkey` does not exist. + /// @param key Key to be renamed. + /// @param newkey The new name of the key. + /// @return Whether key has been renamed. + /// @retval true If key has been renamed. + /// @retval false If newkey already exists. + /// @see https://redis.io/commands/renamenx + bool renamenx(const StringView &key, const StringView &newkey); + + /// @brief Create a key with the value obtained by `Redis::dump`. + /// @param key Key. + /// @param val Value obtained by `Redis::dump`. + /// @param ttl Timeout of the created key in milliseconds. If `ttl` is 0, set no timeout. + /// @param replace Whether to overwrite an existing key. + /// If `replace` is `true` and key already exists, throw an exception. + /// @see https://redis.io/commands/restore + void restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false); + + /// @brief Create a key with the value obtained by `Redis::dump`. + /// @param key Key. + /// @param val Value obtained by `Redis::dump`. + /// @param ttl Timeout of the created key in milliseconds. If `ttl` is 0, set no timeout. + /// @param replace Whether to overwrite an existing key. + /// If `replace` is `true` and key already exists, throw an exception. + /// @see https://redis.io/commands/restore + void restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false); + + // TODO: sort + + /// @brief Scan keys of the database matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::unordered_set keys; + /// while (true) { + /// cursor = redis.scan(cursor, "pattern:*", 10, std::inserter(keys, keys.begin())); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param cursor Cursor. + /// @param pattern Pattern of the keys to be scanned. + /// @param count A hint for how many keys to be scanned. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + /// TODO: support the TYPE option for Redis 6.0. + template + long long scan(long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan all keys of the database. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + template + long long scan(long long cursor, + Output output); + + /// @brief Scan keys of the database matching the given pattern. + /// @param cursor Cursor. + /// @param pattern Pattern of the keys to be scanned. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + template + long long scan(long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan keys of the database matching the given pattern. + /// @param cursor Cursor. + /// @param count A hint for how many keys to be scanned. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + template + long long scan(long long cursor, + long long count, + Output output); + + /// @brief Update the last access time of the given key. + /// @param key Key. + /// @return Whether last access time of the key has been updated. + /// @retval 1 If key exists, and last access time has been updated. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/touch + long long touch(const StringView &key); + + /// @brief Update the last access time of the given key. + /// @param first Iterator to the first key to be touched. + /// @param last Off-the-end iterator to the given range. + /// @return Whether last access time of the key has been updated. + /// @retval 1 If key exists, and last access time has been updated. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/touch + template + long long touch(Input first, Input last); + + /// @brief Update the last access time of the given key. + /// @param il Initializer list of keys to be touched. + /// @return Whether last access time of the key has been updated. + /// @retval 1 If key exists, and last access time has been updated. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/touch + template + long long touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + /// @brief Get the remaining Time-To-Live of a key. + /// @param key Key. + /// @return TTL in seconds. + /// @retval TTL If the key has a timeout. + /// @retval -1 If the key exists but does not have a timeout. + /// @retval -2 If the key does not exist. + /// @note In Redis 2.6 or older, `ttl` returns -1 if the key does not exist, + /// or if the key exists but does not have a timeout. + /// @see https://redis.io/commands/ttl + long long ttl(const StringView &key); + + /// @brief Get the type of the value stored at key. + /// @param key Key. + /// @return The type of the value. + /// @see https://redis.io/commands/type + std::string type(const StringView &key); + + /// @brief Remove the given key asynchronously, i.e. without blocking Redis. + /// @param key Key. + /// @return Whether the key has been removed. + /// @retval 1 If key exists, and has been removed. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/unlink + long long unlink(const StringView &key); + + /// @brief Remove the given keys asynchronously, i.e. without blocking Redis. + /// @param first Iterator to the first key in the given range. + /// @param last Off-the-end iterator to the given range. + /// @return Number of keys that have been removed. + /// @see https://redis.io/commands/unlink + template + long long unlink(Input first, Input last); + + /// @brief Remove the given keys asynchronously, i.e. without blocking Redis. + /// @param il Initializer list of keys to be unlinked. + /// @return Number of keys that have been removed. + /// @see https://redis.io/commands/unlink + template + long long unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + /// @brief Wait until previous write commands are successfully replicated to at + /// least the specified number of replicas or the given timeout has been reached. + /// @param numslaves Number of replicas. + /// @param timeout Timeout in milliseconds. If timeout is 0ms, wait forever. + /// @return Number of replicas that have been successfully replicated these write commands. + /// @note The return value might be less than `numslaves`, because timeout has been reached. + /// @see https://redis.io/commands/wait + long long wait(long long numslaves, long long timeout); + + /// @brief Wait until previous write commands are successfully replicated to at + /// least the specified number of replicas or the given timeout has been reached. + /// @param numslaves Number of replicas. + /// @param timeout Timeout in milliseconds. If timeout is 0ms, wait forever. + /// @return Number of replicas that have been successfully replicated these write commands. + /// @note The return value might be less than `numslaves`, because timeout has been reached. + /// @see https://redis.io/commands/wait + /// TODO: Support default parameter for `timeout` to wait forever. + long long wait(long long numslaves, const std::chrono::milliseconds &timeout); + + // STRING commands. + + /// @brief Append the given string to the string stored at key. + /// @param key Key. + /// @param str String to be appended. + /// @return The length of the string after the append operation. + /// @see https://redis.io/commands/append + long long append(const StringView &key, const StringView &str); + + /// @brief Get the number of bits that have been set for the given range of the string. + /// @param key Key. + /// @param start Start index (inclusive) of the range. 0 means the beginning of the string. + /// @param end End index (inclusive) of the range. -1 means the end of the string. + /// @return Number of bits that have been set. + /// @note The index can be negative to index from the end of the string. + /// @see https://redis.io/commands/bitcount + long long bitcount(const StringView &key, long long start = 0, long long end = -1); + + /// @brief Do bit operation on the string stored at `key`, and save the result to `destination`. + /// @param op Bit operations. + /// @param destination The destination key where the result is saved. + /// @param key The key where the string to be operated is stored. + /// @return The length of the string saved at `destination`. + /// @see https://redis.io/commands/bitop + /// @see `BitOp` + long long bitop(BitOp op, const StringView &destination, const StringView &key); + + /// @brief Do bit operations on the strings stored at the given keys, + /// and save the result to `destination`. + /// @param op Bit operations. + /// @param destination The destination key where the result is saved. + /// @param first Iterator to the first key where the string to be operated is stored. + /// @param last Off-the-end iterator to the given range of keys. + /// @return The length of the string saved at `destination`. + /// @see https://redis.io/commands/bitop + /// @see `BitOp` + template + long long bitop(BitOp op, const StringView &destination, Input first, Input last); + + /// @brief Do bit operations on the strings stored at the given keys, + /// and save the result to `destination`. + /// @param op Bit operations. + /// @param destination The destination key where the result is saved. + /// @param il Initializer list of keys where the strings are operated. + /// @return The length of the string saved at `destination`. + /// @see https://redis.io/commands/bitop + /// @see `BitOp` + template + long long bitop(BitOp op, const StringView &destination, std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + /// @brief Get the position of the first bit set to 0 or 1 in the given range of the string. + /// @param key Key. + /// @param bit 0 or 1. + /// @param start Start index (inclusive) of the range. 0 means the beginning of the string. + /// @param end End index (inclusive) of the range. -1 means the end of the string. + /// @return The position of the first bit set to 0 or 1. + /// @see https://redis.io/commands/bitpos + long long bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1); + + /// @brief Decrement the integer stored at key by 1. + /// @param key Key. + /// @return The value after the decrement. + /// @see https://redis.io/commands/decr + long long decr(const StringView &key); + + /// @brief Decrement the integer stored at key by `decrement`. + /// @param key Key. + /// @param decrement Decrement. + /// @return The value after the decrement. + /// @see https://redis.io/commands/decrby + long long decrby(const StringView &key, long long decrement); + + /// @brief Get the string value stored at key. + /// + /// Example: + /// @code{.cpp} + /// auto val = redis.get("key"); + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "key not exist" << std::endl; + /// @endcode + /// @param key Key. + /// @return The value stored at key. + /// @note If key does not exist, `get` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/get + OptionalString get(const StringView &key); + + /// @brief Get the bit value at offset in the string. + /// @param key Key. + /// @param offset Offset. + /// @return The bit value. + /// @see https://redis.io/commands/getbit + long long getbit(const StringView &key, long long offset); + + /// @brief Get the substring of the string stored at key. + /// @param key Key. + /// @param start Start index (inclusive) of the range. 0 means the beginning of the string. + /// @param end End index (inclusive) of the range. -1 means the end of the string. + /// @return The substring in range [start, end]. If key does not exist, return an empty string. + /// @see https://redis.io/commands/getrange + std::string getrange(const StringView &key, long long start, long long end); + + /// @brief Atomically set the string stored at `key` to `val`, and return the old value. + /// @param key Key. + /// @param val Value to be set. + /// @return The old value stored at key. + /// @note If key does not exist, `getset` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/getset + /// @see `OptionalString` + OptionalString getset(const StringView &key, const StringView &val); + + /// @brief Increment the integer stored at key by 1. + /// @param key Key. + /// @return The value after the increment. + /// @see https://redis.io/commands/incr + long long incr(const StringView &key); + + /// @brief Increment the integer stored at key by `increment`. + /// @param key Key. + /// @param increment Increment. + /// @return The value after the increment. + /// @see https://redis.io/commands/incrby + long long incrby(const StringView &key, long long increment); + + /// @brief Increment the floating point number stored at key by `increment`. + /// @param key Key. + /// @param increment Increment. + /// @return The value after the increment. + /// @see https://redis.io/commands/incrbyfloat + double incrbyfloat(const StringView &key, double increment); + + /// @brief Get the values of multiple keys atomically. + /// + /// Example: + /// @code{.cpp} + /// std::vector keys = {"k1", "k2", "k3"}; + /// std::vector vals; + /// redis.mget(keys.begin(), keys.end(), std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "key does not exist" << std::endl; + /// } + /// @endcode + /// @param first Iterator to the first key of the given range. + /// @param last Off-the-end iterator to the given range. + /// @param output Output iterator to the destination where the values are stored. + /// @note The destination should be a container of `OptionalString` type, + /// since the given key might not exist (in this case, the value of the corresponding + /// key is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/mget + template + void mget(Input first, Input last, Output output); + + /// @brief Get the values of multiple keys atomically. + /// + /// Example: + /// @code{.cpp} + /// std::vector vals; + /// redis.mget({"k1", "k2", "k3"}, std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "key does not exist" << std::endl; + /// } + /// @endcode + /// @param il Initializer list of keys. + /// @param output Output iterator to the destination where the values are stored. + /// @note The destination should be a container of `OptionalString` type, + /// since the given key might not exist (in this case, the value of the corresponding + /// key is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/mget + template + void mget(std::initializer_list il, Output output) { + mget(il.begin(), il.end(), output); + } + + /// @brief Set multiple key-value pairs. + /// + /// Example: + /// @code{.cpp} + /// std::vector> kvs1 = {{"k1", "v1"}, {"k2", "v2"}}; + /// redis.mset(kvs1.begin(), kvs1.end()); + /// std::unordered_map kvs2 = {{"k3", "v3"}, {"k4", "v4"}}; + /// redis.mset(kvs2.begin(), kvs2.end()); + /// @endcode + /// @param first Iterator to the first key-value pair. + /// @param last Off-the-end iterator to the given range. + /// @see https://redis.io/commands/mset + template + void mset(Input first, Input last); + + /// @brief Set multiple key-value pairs. + /// + /// Example: + /// @code{.cpp} + /// redis.mset({std::make_pair("k1", "v1"), std::make_pair("k2", "v2")}); + /// @endcode + /// @param il Initializer list of key-value pairs. + /// @see https://redis.io/commands/mset + template + void mset(std::initializer_list il) { + mset(il.begin(), il.end()); + } + + /// @brief Set the given key-value pairs if all specified keys do not exist. + /// + /// Example: + /// @code{.cpp} + /// std::vector> kvs1; + /// redis.msetnx(kvs1.begin(), kvs1.end()); + /// std::unordered_map kvs2; + /// redis.msetnx(kvs2.begin(), kvs2.end()); + /// @endcode + /// @param first Iterator to the first key-value pair. + /// @param last Off-the-end iterator of the given range. + /// @return Whether all keys have been set. + /// @retval true If all keys have been set. + /// @retval false If no key was set, i.e. at least one key already exist. + /// @see https://redis.io/commands/msetnx + template + bool msetnx(Input first, Input last); + + /// @brief Set the given key-value pairs if all specified keys do not exist. + /// + /// Example: + /// @code{.cpp} + /// redis.msetnx({make_pair("k1", "v1"), make_pair("k2", "v2")}); + /// @endcode + /// @param il Initializer list of key-value pairs. + /// @return Whether all keys have been set. + /// @retval true If all keys have been set. + /// @retval false If no key was set, i.e. at least one key already exist. + /// @see https://redis.io/commands/msetnx + template + bool msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + /// @brief Set key-value pair with the given timeout in milliseconds. + /// @param key Key. + /// @param ttl Time-To-Live in milliseconds. + /// @param val Value. + /// @see https://redis.io/commands/psetex + void psetex(const StringView &key, + long long ttl, + const StringView &val); + + /// @brief Set key-value pair with the given timeout in milliseconds. + /// @param key Key. + /// @param ttl Time-To-Live in milliseconds. + /// @param val Value. + /// @see https://redis.io/commands/psetex + void psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val); + + /// @brief Set a key-value pair. + /// + /// Example: + /// @code{.cpp} + /// // Set a key-value pair. + /// redis.set("key", "value"); + /// // Set a key-value pair, and expire it after 10 seconds. + /// redis.set("key", "value", std::chrono::seconds(10)); + /// // Set a key-value pair with a timeout, only if the key already exists. + /// if (redis.set("key", "value", std::chrono::seconds(10), UpdateType::EXIST)) + /// std::cout << "OK" << std::endl; + /// else + /// std::cout << "key does not exist" << std::endl; + /// @endcode + /// @param key Key. + /// @param val Value. + /// @param ttl Timeout on the key. If `ttl` is 0ms, do not set timeout. + /// @param type Options for set command: + /// - UpdateType::EXIST: Set the key only if it already exists. + /// - UpdateType::NOT_EXIST: Set the key only if it does not exist. + /// - UpdateType::ALWAYS: Always set the key no matter whether it exists. + /// @return Whether the key has been set. + /// @retval true If the key has been set. + /// @retval false If the key was not set, because of the given option. + /// @see https://redis.io/commands/set + // TODO: Support KEEPTTL option for Redis 6.0 + bool set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS); + + // TODO: add SETBIT command. + + /// @brief Set key-value pair with the given timeout in seconds. + /// @param key Key. + /// @param ttl Time-To-Live in seconds. + /// @param val Value. + /// @see https://redis.io/commands/setex + void setex(const StringView &key, + long long ttl, + const StringView &val); + + /// @brief Set key-value pair with the given timeout in seconds. + /// @param key Key. + /// @param ttl Time-To-Live in seconds. + /// @param val Value. + /// @see https://redis.io/commands/setex + void setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val); + + /// @brief Set the key if it does not exist. + /// @param key Key. + /// @param val Value. + /// @return Whether the key has been set. + /// @retval true If the key has been set. + /// @retval false If the key was not set, i.e. the key already exists. + /// @see https://redis.io/commands/setnx + bool setnx(const StringView &key, const StringView &val); + + /// @brief Set the substring starting from `offset` to the given value. + /// @param key Key. + /// @param offset Offset. + /// @param val Value. + /// @return The length of the string after this operation. + /// @see https://redis.io/commands/setrange + long long setrange(const StringView &key, long long offset, const StringView &val); + + /// @brief Get the length of the string stored at key. + /// @param key Key. + /// @return The length of the string. + /// @note If key does not exist, `strlen` returns 0. + /// @see https://redis.io/commands/strlen + long long strlen(const StringView &key); + + // LIST commands. + + /// @brief Pop the first element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + OptionalStringPair blpop(const StringView &key, long long timeout); + + /// @brief Pop the first element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + OptionalStringPair blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(Input first, Input last, long long timeout); + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param il Initializer list of keys. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param il Initializer list of keys. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop the last element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + OptionalStringPair brpop(const StringView &key, long long timeout); + + /// @brief Pop the last element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + OptionalStringPair brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(Input first, Input last, long long timeout); + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param il Initializer list of lists. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param first Iterator to the first list. + /// @param last Off-the-end iterator to the list range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param il Initializer list of list keys. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop last element of one list and push it to the left of another list in blocking way. + /// @param source Key of the source list. + /// @param destination Key of the destination list. + /// @param timeout Timeout. 0 means block forever. + /// @return The popped element. + /// @note If the source list does not exist, `brpoplpush` returns `OptionalString{}` (`std::nullopt`). + /// @see `Redis::rpoplpush` + /// @see https://redis.io/commands/brpoplpush + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + long long timeout); + + /// @brief Pop last element of one list and push it to the left of another list in blocking way. + /// @param source Key of the source list. + /// @param destination Key of the destination list. + /// @param timeout Timeout. 0 means block forever. + /// @return The popped element. + /// @note If the source list does not exist, `brpoplpush` returns `OptionalString{}` (`std::nullopt`). + /// @see `Redis::rpoplpush` + /// @see https://redis.io/commands/brpoplpush + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Get the element at the given index of the list. + /// @param key Key where the list is stored. + /// @param index Zero-base index, and -1 means the last element. + /// @return The element at the given index. + /// @see https://redis.io/commands/lindex + OptionalString lindex(const StringView &key, long long index); + + /// @brief Insert an element to a list before or after the pivot element. + /// + /// Example: + /// @code{.cpp} + /// // Insert 'hello' before 'world' + /// auto len = redis.linsert("list", InsertPosition::BEFORE, "world", "hello"); + /// if (len == -1) + /// std::cout << "there's no 'world' in the list" << std::endl; + /// else + /// std::cout << "after the operation, the length of the list is " << len << std::endl; + /// @endcode + /// @param key Key where the list is stored. + /// @param position Before or after the pivot element. + /// @param pivot The pivot value. The `pivot` is the value of the element, not the index. + /// @param val Element to be inserted. + /// @return The length of the list after the operation. + /// @note If the pivot value is not found, `linsert` returns -1. + /// @see `InsertPosition` + /// @see https://redis.io/commands/linsert + long long linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + + /// @brief Get the length of the list. + /// @param key Key where the list is stored. + /// @return The length of the list. + /// @see https://redis.io/commands/llen + long long llen(const StringView &key); + + /// @brief Pop the first element of the list. + /// + /// Example: + /// @code{.cpp} + /// auto element = redis.lpop("list"); + /// if (element) + /// std::cout << *element << std::endl; + /// else + /// std::cout << "list is empty, i.e. list does not exist" << std::endl; + /// @endcode + /// @param key Key where the list is stored. + /// @return The popped element. + /// @note If list is empty, i.e. key does not exist, return `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/lpop + OptionalString lpop(const StringView &key); + + /// @brief Push an element to the beginning of the list. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpush + long long lpush(const StringView &key, const StringView &val); + + /// @brief Push multiple elements to the beginning of the list. + /// + /// Example: + /// @code{.cpp} + /// std::vector elements = {"e1", "e2", "e3"}; + /// redis.lpush("list", elements.begin(), elements.end()); + /// @endcode + /// @param key Key where the list is stored. + /// @param first Iterator to the first element to be pushed. + /// @param last Off-the-end iterator to the given element range. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpush + template + long long lpush(const StringView &key, Input first, Input last); + + /// @brief Push multiple elements to the beginning of the list. + /// + /// Example: + /// @code{.cpp} + /// redis.lpush("list", {"e1", "e2", "e3"}); + /// @endcode + /// @param key Key where the list is stored. + /// @param il Initializer list of elements. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpush + template + long long lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + /// @brief Push an element to the beginning of the list, only if the list already exists. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpushx + // TODO: add a multiple elements overload. + long long lpushx(const StringView &key, const StringView &val); + + /// @brief Get elements in the given range of the given list. + /// + /// Example: + /// @code{.cpp} + /// std::vector elements; + /// // Save all elements of a Redis list to a vector of string. + /// redis.lrange("list", 0, -1, std::back_inserter(elements)); + /// @endcode + /// @param key Key where the list is stored. + /// @param start Start index of the range. Index can be negative, which mean index from the end. + /// @param stop End index of the range. + /// @param output Output iterator to the destination where the results are saved. + /// @see https://redis.io/commands/lrange + template + void lrange(const StringView &key, long long start, long long stop, Output output); + + /// @brief Remove the first `count` occurrences of elements equal to `val`. + /// @param key Key where the list is stored. + /// @param count Number of occurrences to be removed. + /// @param val Value. + /// @return Number of elements removed. + /// @note `count` can be positive, negative and 0. Check the reference for detail. + /// @see https://redis.io/commands/lrem + long long lrem(const StringView &key, long long count, const StringView &val); + + /// @brief Set the element at the given index to the specified value. + /// @param key Key where the list is stored. + /// @param index Index of the element to be set. + /// @param val Value. + /// @see https://redis.io/commands/lset + void lset(const StringView &key, long long index, const StringView &val); + + /// @brief Trim a list to keep only element in the given range. + /// @param key Key where the key is stored. + /// @param start Start of the index. + /// @param stop End of the index. + /// @see https://redis.io/commands/ltrim + void ltrim(const StringView &key, long long start, long long stop); + + /// @brief Pop the last element of a list. + /// @param key Key where the list is stored. + /// @return The popped element. + /// @note If the list is empty, i.e. key does not exist, `rpop` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/rpop + OptionalString rpop(const StringView &key); + + /// @brief Pop last element of one list and push it to the left of another list. + /// @param source Key of the source list. + /// @param destination Key of the destination list. + /// @return The popped element. + /// @note If the source list does not exist, `rpoplpush` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/brpoplpush + OptionalString rpoplpush(const StringView &source, const StringView &destination); + + /// @brief Push an element to the end of the list. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpush + long long rpush(const StringView &key, const StringView &val); + + /// @brief Push multiple elements to the end of the list. + /// @param key Key where the list is stored. + /// @param first Iterator to the first element to be pushed. + /// @param last Off-the-end iterator to the given element range. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpush + template + long long rpush(const StringView &key, Input first, Input last); + + /// @brief Push multiple elements to the end of the list. + /// @param key Key where the list is stored. + /// @param il Initializer list of elements to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpush + template + long long rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + /// @brief Push an element to the end of the list, only if the list already exists. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpushx + long long rpushx(const StringView &key, const StringView &val); + + // HASH commands. + + /// @brief Remove the given field from hash. + /// @param key Key where the hash is stored. + /// @param field Field to be removed. + /// @return Whether the field has been removed. + /// @retval 1 If the field exists, and has been removed. + /// @retval 0 If the field does not exist. + /// @see https://redis.io/commands/hdel + long long hdel(const StringView &key, const StringView &field); + + /// @brief Remove multiple fields from hash. + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field to be removed. + /// @param last Off-the-end iterator to the given field range. + /// @return Number of fields that has been removed. + /// @see https://redis.io/commands/hdel + template + long long hdel(const StringView &key, Input first, Input last); + + /// @brief Remove multiple fields from hash. + /// @param key Key where the hash is stored. + /// @param il Initializer list of fields. + /// @return Number of fields that has been removed. + /// @see https://redis.io/commands/hdel + template + long long hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + /// @brief Check if the given field exists in hash. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @return Whether the field exists. + /// @retval true If the field exists in hash. + /// @retval false If the field does not exist. + /// @see https://redis.io/commands/hexists + bool hexists(const StringView &key, const StringView &field); + + /// @brief Get the value of the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @return Value of the given field. + /// @note If field does not exist, `hget` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/hget + OptionalString hget(const StringView &key, const StringView &field); + + /// @brief Get all field-value pairs of the given hash. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map results; + /// // Save all field-value pairs of a Redis hash to an unordered_map. + /// redis.hgetall("hash", std::inserter(results, results.begin())); + /// @endcode + /// @param key Key where the hash is stored. + /// @param output Output iterator to the destination where the result is saved. + /// @note It's always a bad idea to call `hgetall` on a large hash, since it will block Redis. + /// @see `Redis::hscan` + /// @see https://redis.io/commands/hgetall + template + void hgetall(const StringView &key, Output output); + + /// @brief Increment the integer stored at the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param increment Increment. + /// @return The value of the field after the increment. + /// @see https://redis.io/commands/hincrby + long long hincrby(const StringView &key, const StringView &field, long long increment); + + /// @brief Increment the floating point number stored at the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param increment Increment. + /// @return The value of the field after the increment. + /// @see https://redis.io/commands/hincrbyfloat + double hincrbyfloat(const StringView &key, const StringView &field, double increment); + + /// @brief Get all fields of the given hash. + /// @param key Key where the hash is stored. + /// @param output Output iterator to the destination where the result is saved. + /// @note It's always a bad idea to call `hkeys` on a large hash, since it will block Redis. + /// @see `Redis::hscan` + /// @see https://redis.io/commands/hkeys + template + void hkeys(const StringView &key, Output output); + + /// @brief Get the number of fields of the given hash. + /// @param key Key where the hash is stored. + /// @return Number of fields. + /// @see https://redis.io/commands/hlen + long long hlen(const StringView &key); + + /// @brief Get values of multiple fields. + /// + /// Example: + /// @code{.cpp} + /// std::vector fields = {"f1", "f2"}; + /// std::vector vals; + /// redis.hmget("hash", fields.begin(), fields.end(), std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "field not exist" << std::endl; + /// } + /// @endcode + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field. + /// @param last Off-the-end iterator to the given field range. + /// @param output Output iterator to the destination where the result is saved. + /// @note The destination should be a container of `OptionalString` type, + /// since the given field might not exist (in this case, the value of the corresponding + /// field is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/hmget + template + void hmget(const StringView &key, Input first, Input last, Output output); + + /// @brief Get values of multiple fields. + /// + /// Example: + /// @code{.cpp} + /// std::vector vals; + /// redis.hmget("hash", {"f1", "f2"}, std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "field not exist" << std::endl; + /// } + /// @endcode + /// @param key Key where the hash is stored. + /// @param il Initializer list of fields. + /// @param output Output iterator to the destination where the result is saved. + /// @note The destination should be a container of `OptionalString` type, + /// since the given field might not exist (in this case, the value of the corresponding + /// field is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/hmget + template + void hmget(const StringView &key, std::initializer_list il, Output output) { + hmget(key, il.begin(), il.end(), output); + } + + /// @brief Set multiple field-value pairs of the given hash. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map m = {{"f1", "v1"}, {"f2", "v2"}}; + /// redis.hmset("hash", m.begin(), m.end()); + /// @endcode + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field-value pair. + /// @param last Off-the-end iterator to the range. + /// @see https://redis.io/commands/hmset + template + void hmset(const StringView &key, Input first, Input last); + + /// @brief Set multiple field-value pairs of the given hash. + /// + /// Example: + /// @code{.cpp} + /// redis.hmset("hash", {std::make_pair("f1", "v1"), std::make_pair("f2", "v2")}); + /// @endcode + /// @param key Key where the hash is stored. + /// @param il Initializer list of field-value pairs. + /// @see https://redis.io/commands/hmset + template + void hmset(const StringView &key, std::initializer_list il) { + hmset(key, il.begin(), il.end()); + } + + /// @brief Scan fields of the given hash matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::unordered_map kvs; + /// while (true) { + /// cursor = redis.hscan("hash", cursor, "pattern:*", 10, std::inserter(kvs, kvs.begin())); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan fields of the given hash matching the given pattern. + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan all fields of the given hash. + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + long long count, + Output output); + + /// @brief Scan all fields of the given hash. + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + Output output); + + /// @brief Set hash field to value. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param val Value. + /// @return Whether the given field is a new field. + /// @retval true If the given field didn't exist, and a new field has been added. + /// @retval false If the given field already exists, and its value has been overwritten. + /// @note When `hset` returns false, it does not mean that the method failed to set the field. + /// Instead, it means that the field already exists, and we've overwritten its value. + /// If `hset` fails, it will throw an exception of `Exception` type. + /// @see https://github.com/sewenew/redis-plus-plus/issues/9 + /// @see https://redis.io/commands/hset + bool hset(const StringView &key, const StringView &field, const StringView &val); + + /// @brief Set hash field to value. + /// @param key Key where the hash is stored. + /// @param item The field-value pair to be set. + /// @return Whether the given field is a new field. + /// @retval true If the given field didn't exist, and a new field has been added. + /// @retval false If the given field already exists, and its value has been overwritten. + /// @note When `hset` returns false, it does not mean that the method failed to set the field. + /// Instead, it means that the field already exists, and we've overwritten its value. + /// If `hset` fails, it will throw an exception of `Exception` type. + /// @see https://github.com/sewenew/redis-plus-plus/issues/9 + /// @see https://redis.io/commands/hset + bool hset(const StringView &key, const std::pair &item); + + /// @brief Set multiple fields of the given hash. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map m = {{"f1", "v1"}, {"f2", "v2"}}; + /// redis.hset("hash", m.begin(), m.end()); + /// @endcode + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field to be set. + /// @param last Off-the-end iterator to the given range. + /// @return Number of fields that have been added, i.e. fields that not existed before. + /// @see https://redis.io/commands/hset + template + auto hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, long long>::type; + + /// @brief Set multiple fields of the given hash. + /// + /// Example: + /// @code{.cpp} + /// redis.hset("hash", {std::make_pair("f1", "v1"), std::make_pair("f2", "v2")}); + /// @endcode + /// @param key Key where the hash is stored. + /// @param il Initializer list of field-value pairs. + /// @return Number of fields that have been added, i.e. fields that not existed before. + /// @see https://redis.io/commands/hset + template + long long hset(const StringView &key, std::initializer_list il) { + return hset(key, il.begin(), il.end()); + } + + /// @brief Set hash field to value, only if the given field does not exist. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param val Value. + /// @return Whether the field has been set. + /// @retval true If the field has been set. + /// @retval false If failed to set the field, i.e. the field already exists. + /// @see https://redis.io/commands/hsetnx + bool hsetnx(const StringView &key, const StringView &field, const StringView &val); + + /// @brief Set hash field to value, only if the given field does not exist. + /// @param key Key where the hash is stored. + /// @param item The field-value pair to be set. + /// @return Whether the field has been set. + /// @retval true If the field has been set. + /// @retval false If failed to set the field, i.e. the field already exists. + /// @see https://redis.io/commands/hsetnx + bool hsetnx(const StringView &key, const std::pair &item); + + /// @brief Get the length of the string stored at the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @return Length of the string. + /// @see https://redis.io/commands/hstrlen + long long hstrlen(const StringView &key, const StringView &field); + + /// @brief Get values of all fields stored at the given hash. + /// @param key Key where the hash is stored. + /// @param output Output iterator to the destination where the result is saved. + /// @note It's always a bad idea to call `hvals` on a large hash, since it might block Redis. + /// @see `Redis::hscan` + /// @see https://redis.io/commands/hvals + template + void hvals(const StringView &key, Output output); + + // SET commands. + + /// @brief Add a member to the given set. + /// @param key Key where the set is stored. + /// @param member Member to be added. + /// @return Whether the given member is a new member. + /// @retval 1 The member did not exist before, and it has been added now. + /// @retval 0 The member already exists before this operation. + /// @see https://redis.io/commands/sadd + long long sadd(const StringView &key, const StringView &member); + + /// @brief Add multiple members to the given set. + /// @param key Key where the set is stored. + /// @param first Iterator to the first member to be added. + /// @param last Off-the-end iterator to the member range. + /// @return Number of new members that have been added, i.e. members did not exist before. + /// @see https://redis.io/commands/sadd + template + long long sadd(const StringView &key, Input first, Input last); + + /// @brief Add multiple members to the given set. + /// @param key Key where the set is stored. + /// @param il Initializer list of members to be added. + /// @return Number of new members that have been added, i.e. members did not exist before. + /// @see https://redis.io/commands/sadd + template + long long sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + /// @brief Get the number of members in the set. + /// @param key Key where the set is stored. + /// @return Number of members. + /// @see https://redis.io/commands/scard + long long scard(const StringView &key); + + /// @brief Get the difference between the first set and all successive sets. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to the range. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sdiff + // TODO: `void sdiff(const StringView &key, Input first, Input last, Output output)` is better. + template + void sdiff(Input first, Input last, Output output); + + /// @brief Get the difference between the first set and all successive sets. + /// @param il Initializer list of sets. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sdiff + template + void sdiff(std::initializer_list il, Output output) { + sdiff(il.begin(), il.end(), output); + } + + /// @brief Copy set stored at `key` to `destination`. + /// @param destination Key of the destination set. + /// @param key Key of the source set. + /// @return Number of members of the set. + /// @see https://redis.io/commands/sdiffstore + long long sdiffstore(const StringView &destination, const StringView &key); + + /// @brief Same as `sdiff`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to set range. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sdiffstore + template + long long sdiffstore(const StringView &destination, + Input first, + Input last); + + /// @brief Same as `sdiff`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param il Initializer list of sets. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sdiffstore + template + long long sdiffstore(const StringView &destination, + std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + /// @brief Get the intersection between the first set and all successive sets. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to the range. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sinter + // TODO: `void sinter(const StringView &key, Input first, Input last, Output output)` is better. + template + void sinter(Input first, Input last, Output output); + + /// @brief Get the intersection between the first set and all successive sets. + /// @param il Initializer list of sets. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sinter + template + void sinter(std::initializer_list il, Output output) { + sinter(il.begin(), il.end(), output); + } + + /// @brief Copy set stored at `key` to `destination`. + /// @param destination Key of the destination set. + /// @param key Key of the source set. + /// @return Number of members of the set. + /// @see https://redis.io/commands/sinter + long long sinterstore(const StringView &destination, const StringView &key); + + /// @brief Same as `sinter`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to set range. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sinter + template + long long sinterstore(const StringView &destination, + Input first, + Input last); + + /// @brief Same as `sinter`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param il Initializer list of sets. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sinter + template + long long sinterstore(const StringView &destination, + std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + /// @brief Test if `member` exists in the set stored at key. + /// @param key Key where the set is stored. + /// @param member Member to be checked. + /// @return Whether `member` exists in the set. + /// @retval true If it exists in the set. + /// @retval false If it does not exist in the set, or the given key does not exist. + /// @see https://redis.io/commands/sismember + bool sismember(const StringView &key, const StringView &member); + + /// @brief Get all members in the given set. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_set members1; + /// redis.smembers("set", std::inserter(members1, members1.begin())); + /// std::vector members2; + /// redis.smembers("set", std::back_inserter(members2)); + /// @endcode + /// @param key Key where the set is stored. + /// @param output Iterator to the destination where the result is saved. + /// @see https://redis.io/commands/smembers + template + void smembers(const StringView &key, Output output); + + /// @brief Move `member` from one set to another. + /// @param source Key of the set in which the member currently exists. + /// @param destination Key of the destination set. + /// @return Whether the member has been moved. + /// @retval true If the member has been moved. + /// @retval false If `member` does not exist in `source`. + /// @see https://redis.io/commands/smove + bool smove(const StringView &source, + const StringView &destination, + const StringView &member); + + /// @brief Remove a random member from the set. + /// @param key Key where the set is stored. + /// @return The popped member. + /// @note If the set is empty, `spop` returns `OptionalString{}` (`std::nullopt`). + /// @see `Redis::srandmember` + /// @see https://redis.io/commands/spop + OptionalString spop(const StringView &key); + + /// @brief Remove multiple random members from the set. + /// + /// Example: + /// @code{.cpp} + /// std::vector members; + /// redis.spop("set", 10, std::back_inserter(members)); + /// @endcode + /// @param key Key where the set is stored. + /// @param count Number of members to be popped. + /// @param output Output iterator to the destination where the result is saved. + /// @note The number of popped members might be less than `count`. + /// @see `Redis::srandmember` + /// @see https://redis.io/commands/spop + template + void spop(const StringView &key, long long count, Output output); + + /// @brief Get a random member of the given set. + /// @param key Key where the set is stored. + /// @return A random member. + /// @note If the set is empty, `srandmember` returns `OptionalString{}` (`std::nullopt`). + /// @note This method won't remove the member from the set. + /// @see `Redis::spop` + /// @see https://redis.io/commands/srandmember + OptionalString srandmember(const StringView &key); + + /// @brief Get multiple random members of the given set. + /// @param key Key where the set is stored. + /// @param count Number of members to be returned. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method won't remove members from the set. + /// @see `Redis::spop` + /// @see https://redis.io/commands/srandmember + template + void srandmember(const StringView &key, long long count, Output output); + + /// @brief Remove a member from set. + /// @param key Key where the set is stored. + /// @param member Member to be removed. + /// @return Whether the member has been removed. + /// @retval 1 If the given member exists, and has been removed. + /// @retval 0 If the given member does not exist. + /// @see https://redis.io/commands/srem + long long srem(const StringView &key, const StringView &member); + + /// @brief Remove multiple members from set. + /// @param key Key where the set is stored. + /// @param first Iterator to the first member to be removed. + /// @param last Off-the-end iterator to the range. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/srem + template + long long srem(const StringView &key, Input first, Input last); + + /// @brief Remove multiple members from set. + /// @param key Key where the set is stored. + /// @param il Initializer list of members to be removed. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/srem + template + long long srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + /// @brief Scan members of the set matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::unordered_set members; + /// while (true) { + /// cursor = redis.sscan("set", cursor, "pattern:*", + /// 10, std::inserter(members, members.begin())); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan members of the set matching the given pattern. + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan all members of the given set. + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + long long count, + Output output); + + /// @brief Scan all members of the given set. + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + Output output); + + /// @brief Get the union between the first set and all successive sets. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to the range. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sunion + // TODO: `void sunion(const StringView &key, Input first, Input last, Output output)` is better. + template + void sunion(Input first, Input last, Output output); + + /// @brief Get the union between the first set and all successive sets. + /// @param il Initializer list of sets. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sunion + template + void sunion(std::initializer_list il, Output output) { + sunion(il.begin(), il.end(), output); + } + + /// @brief Copy set stored at `key` to `destination`. + /// @param destination Key of the destination set. + /// @param key Key of the source set. + /// @return Number of members of the set. + /// @see https://redis.io/commands/sunionstore + long long sunionstore(const StringView &destination, const StringView &key); + + /// @brief Same as `sunion`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to set range. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sunionstore + template + long long sunionstore(const StringView &destination, Input first, Input last); + + /// @brief Same as `sunion`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param il Initializer list of sets. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sunionstore + template + long long sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + /// @brief Pop the member with highest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the highest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + auto bzpopmax(const StringView &key, long long timeout) + -> Optional>; + + /// @brief Pop the member with highest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the highest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(Input first, Input last, long long timeout) + -> Optional>; + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + /// @brief Pop the member with lowest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + auto bzpopmin(const StringView &key, long long timeout) + -> Optional>; + + /// @brief Pop the member with lowest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(Input first, Input last, long long timeout) + -> Optional>; + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + /// @brief Add or update a member with score to sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member to be added. + /// @param score Score of the member. + /// @param type Options for zadd command: + /// - UpdateType::EXIST: Add the member only if it already exists. + /// - UpdateType::NOT_EXIST: Add the member only if it does not exist. + /// - UpdateType::ALWAYS: Always add the member no matter whether it exists. + /// @param changed Whether change the return value from number of newly added member to + /// number of members changed (i.e. added and updated). + /// @return Number of added members or number of added and updated members depends on `changed`. + /// @note We don't support the INCR option, because in this case, the return value of zadd + /// command is NOT of type long long. However, you can always use the generic interface + /// to send zadd command with INCR option: + /// `auto score = redis.command("ZADD", "key", "XX", "INCR", 10, "mem");` + /// @see `UpdateType` + /// @see https://redis.io/commands/zadd + long long zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + /// @brief Add or update multiple members with score to sorted set. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map m = {{"m1", 1.2}, {"m2", 2.3}}; + /// redis.zadd("zset", m.begin(), m.end()); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param first Iterator to the first member-score pair. + /// @param last Off-the-end iterator to the member-score pairs range. + /// @param type Options for zadd command: + /// - UpdateType::EXIST: Add the member only if it already exists. + /// - UpdateType::NOT_EXIST: Add the member only if it does not exist. + /// - UpdateType::ALWAYS: Always add the member no matter whether it exists. + /// @param changed Whether change the return value from number of newly added member to + /// number of members changed (i.e. added and updated). + /// @return Number of added members or number of added and updated members depends on `changed`. + /// @note We don't support the INCR option, because in this case, the return value of zadd + /// command is NOT of type long long. However, you can always use the generic interface + /// to send zadd command with INCR option: + /// `auto score = redis.command("ZADD", "key", "XX", "INCR", 10, "mem");` + /// @see `UpdateType` + /// @see https://redis.io/commands/zadd + template + long long zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + /// @brief Add or update multiple members with score to sorted set. + /// + /// Example: + /// @code{.cpp} + /// redis.zadd("zset", {std::make_pair("m1", 1.4), std::make_pair("m2", 2.3)}); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param first Iterator to the first member-score pair. + /// @param last Off-the-end iterator to the member-score pairs range. + /// @param type Options for zadd command: + /// - UpdateType::EXIST: Add the member only if it already exists. + /// - UpdateType::NOT_EXIST: Add the member only if it does not exist. + /// - UpdateType::ALWAYS: Always add the member no matter whether it exists. + /// @param changed Whether change the return value from number of newly added member to + /// number of members changed (i.e. added and updated). + /// @return Number of added members or number of added and updated members depends on `changed`. + /// @note We don't support the INCR option, because in this case, the return value of zadd + /// command is NOT of type long long. However, you can always use the generic interface + /// to send zadd command with INCR option: + /// `auto score = redis.command("ZADD", "key", "XX", "INCR", 10, "mem");` + /// @see `UpdateType` + /// @see https://redis.io/commands/zadd + template + long long zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + /// @brief Get the number of members in the sorted set. + /// @param key Key where the sorted set is stored. + /// @return Number of members in the sorted set. + /// @see https://redis.io/commands/zcard + long long zcard(const StringView &key); + + /// @brief Get the number of members with score between a min-max score range. + /// + /// Example: + /// @code{.cpp} + /// // Count members with score between (2.3, 5] + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::LEFT_OPEN)); + /// // Count members with score between [2.3, 5) + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::RIGHT_OPEN)); + /// // Count members with score between (2.3, 5) + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::OPEN)); + /// // Count members with score between [2.3, 5] + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::CLOSED)); + /// // Count members with score between [2.3, +inf) + /// redis.zcount("zset", LeftBoundedInterval(2.3, BoundType::RIGHT_OPEN)); + /// // Count members with score between (2.3, +inf) + /// redis.zcount("zset", LeftBoundedInterval(2.3, BoundType::OPEN)); + /// // Count members with score between (-inf, 5] + /// redis.zcount("zset", RightBoundedInterval(5, BoundType::LEFT_OPEN)); + /// // Count members with score between (-inf, 5) + /// redis.zcount("zset", RightBoundedInterval(5, BoundType::OPEN)); + /// // Count members with score between (-inf, +inf) + /// redis.zcount("zset", UnboundedInterval{}); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval The min-max score range. + /// @return Number of members with score between a min-max score range. + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see https://redis.io/commands/zcount + // TODO: add a string version of Interval: zcount("key", "(2.3", "5"). + template + long long zcount(const StringView &key, const Interval &interval); + + /// @brief Increment the score of given member. + /// @param key Key where the sorted set is stored. + /// @param increment Increment. + /// @param member Member. + /// @return The score of the member after the operation. + /// @see https://redis.io/commands/zincrby + double zincrby(const StringView &key, double increment, const StringView &member); + + /// @brief Copy a sorted set to another one with the scores being multiplied by a factor. + /// @param destination Key of the destination sorted set. + /// @param key Key of the source sorted set. + /// @param weight Weight to be multiplied to the score of each member. + /// @return The number of members in the sorted set. + /// @note There's no aggregation type parameter for single key overload, since these 3 types + /// have the same effect. + /// @see `Redis::zunionstore` + /// @see https://redis.io/commands/zinterstore + long long zinterstore(const StringView &destination, const StringView &key, double weight); + + /// @brief Get intersection of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// std::vector keys = {"k1", "k2", "k3"}; + /// redis.zinterstore("destination", keys.begin(), keys.end()); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// std::vector> keys_with_weights = {{"k1", 1}, {"k2", 2}}; + /// redis.zinterstore("destination", keys_with_weights.begin(), + /// keys_with_weights.end(), Aggregation::MIN); + /// // NOTE: `keys_with_weights` can also be of type `std::unordered_map`. + /// // However, it will be slower than std::vector>, since we use + /// // `std::distance(first, last)` to calculate the *numkeys* parameter. + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param first Iterator to the first sorted set (might with weight). + /// @param last Off-the-end iterator to the sorted set range. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `Input` is an + /// iterator to a container of `std::string`, we use the default weight, i.e. 1, and send + /// *ZINTERSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `Input` is an iterator to a container of `std::pair`, + /// i.e. key-weight pair, we send the command with the given weights: + /// *ZINTERSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zunionstore` + /// @see https://redis.io/commands/zinterstore + template + long long zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + /// @brief Get intersection of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// redis.zinterstore("destination", {"k1", "k2"}); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// redis.zinterstore("destination", + /// {std::make_pair("k1", 1), std::make_pair("k2", 2)}, Aggregation::MIN); + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param il Initializer list of sorted set. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `T` is + /// of type `std::string`, we use the default weight, i.e. 1, and send + /// *ZINTERSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `T` is of type `std::pair`, i.e. key-weight pair, + /// we send the command with the given weights: + /// *ZINTERSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zunionstore` + /// @see https://redis.io/commands/zinterstore + template + long long zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + /// @brief Get the number of members between a min-max range in lexicographical order. + /// + /// Example: + /// @code{.cpp} + /// // Count members between (abc, abd] + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::LEFT_OPEN)); + /// // Count members between [abc, abd) + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::RIGHT_OPEN)); + /// // Count members between (abc, abd) + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::OPEN)); + /// // Count members between [abc, abd] + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::CLOSED)); + /// // Count members between [abc, +inf) + /// redis.zlexcount("zset", LeftBoundedInterval("abc", BoundType::RIGHT_OPEN)); + /// // Count members between (abc, +inf) + /// redis.zlexcount("zset", LeftBoundedInterval("abc", BoundType::OPEN)); + /// // Count members between (-inf, "abd"] + /// redis.zlexcount("zset", RightBoundedInterval("abd", BoundType::LEFT_OPEN)); + /// // Count members between (-inf, "abd") + /// redis.zlexcount("zset", RightBoundedInterval("abd", BoundType::OPEN)); + /// // Count members between (-inf, +inf) + /// redis.zlexcount("zset", UnboundedInterval{}); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval The min-max range in lexicographical order. + /// @return Number of members between a min-max range in lexicographical order. + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see https://redis.io/commands/zlexcount + // TODO: add a string version of Interval: zlexcount("key", "(abc", "abd"). + template + long long zlexcount(const StringView &key, const Interval &interval); + + /// @brief Pop the member with highest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @return Member-score pair with the highest score. + /// @note If sorted set is empty `zpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::bzpopmax` + /// @see https://redis.io/commands/zpopmax + Optional> zpopmax(const StringView &key); + + /// @brief Pop multiple members with highest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @param count Number of members to be popped. + /// @param output Output iterator to the destination where the result is saved. + /// @note The number of returned members might be less than `count`. + /// @see `Redis::bzpopmax` + /// @see https://redis.io/commands/zpopmax + template + void zpopmax(const StringView &key, long long count, Output output); + + /// @brief Pop the member with lowest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @return Member-score pair with the lowest score. + /// @note If sorted set is empty `zpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::bzpopmin` + /// @see https://redis.io/commands/zpopmin + Optional> zpopmin(const StringView &key); + + /// @brief Pop multiple members with lowest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @param count Number of members to be popped. + /// @param output Output iterator to the destination where the result is saved. + /// @note The number of returned members might be less than `count`. + /// @see `Redis::bzpopmin` + /// @see https://redis.io/commands/zpopmin + template + void zpopmin(const StringView &key, long long count, Output output); + + /// @brief Get a range of members by rank (ordered from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// // send *ZRANGE* command without the *WITHSCORES* option: + /// std::vector result; + /// redis.zrange("zset", 0, -1, std::back_inserter(result)); + /// // send command with *WITHSCORES* option: + /// std::vector> with_score; + /// redis.zrange("zset", 0, -1, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param start Start rank. Inclusive and can be negative. + /// @param stop Stop rank. Inclusive and can be negative. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZRANGE key start stop* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZRANGE key start stop WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @see `Redis::zrevrange` + /// @see https://redis.io/commands/zrange + template + void zrange(const StringView &key, long long start, long long stop, Output output); + + /// @brief Get a range of members by lexicographical order (from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Get members between [abc, abd]. + /// redis.zrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `Redis::zrevrangebylex` + /// @see https://redis.io/commands/zrangebylex + template + void zrangebylex(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by lexicographical order (from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Limit the result to at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members between [abc, abd]. + /// redis.zrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// opts, std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `LimitOptions` + /// @see `Redis::zrevrangebylex` + /// @see https://redis.io/commands/zrangebylex + template + void zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get a range of members by score (ordered from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Get members whose score between (3, 6). + /// redis.zrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf). + /// redis.zrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrevrangebyscore` + /// @see https://redis.io/commands/zrangebyscore + template + void zrangebyscore(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by score (ordered from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Only return at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members whose score between (3, 6). + /// redis.zrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// opts, std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf). + /// redis.zrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// opts, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrevrangebyscore` + /// @see https://redis.io/commands/zrangebyscore + template + void zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get the rank (from low to high) of the given member in the sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member. + /// @return The rank of the given member. + /// @note If the member does not exist, `zrank` returns `OptionalLongLong{}` (`std::nullopt`). + /// @see https://redis.io/commands/zrank + OptionalLongLong zrank(const StringView &key, const StringView &member); + + /// @brief Remove the given member from sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member to be removed. + /// @return Whether the member has been removed. + /// @retval 1 If the member exists, and has been removed. + /// @retval 0 If the member does not exist. + /// @see https://redis.io/commands/zrem + long long zrem(const StringView &key, const StringView &member); + + /// @brief Remove multiple members from sorted set. + /// @param key Key where the sorted set is stored. + /// @param first Iterator to the first member. + /// @param last Off-the-end iterator to the given range. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/zrem + template + long long zrem(const StringView &key, Input first, Input last); + + /// @brief Remove multiple members from sorted set. + /// @param key Key where the sorted set is stored. + /// @param il Initializer list of members to be removed. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/zrem + template + long long zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + /// @brief Remove members in the given range of lexicographical order. + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @return Number of members removed. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see https://redis.io/commands/zremrangebylex + template + long long zremrangebylex(const StringView &key, const Interval &interval); + + /// @brief Remove members in the given range ordered by rank. + /// @param key Key where the sorted set is stored. + /// @param start Start rank. + /// @param stop Stop rank. + /// @return Number of members removed. + /// @see https://redis.io/commands/zremrangebyrank + long long zremrangebyrank(const StringView &key, long long start, long long stop); + + /// @brief Remove members in the given range ordered by score. + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @return Number of members removed. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see https://redis.io/commands/zremrangebyscore + template + long long zremrangebyscore(const StringView &key, const Interval &interval); + + /// @brief Get a range of members by rank (ordered from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// // send *ZREVRANGE* command without the *WITHSCORES* option: + /// std::vector result; + /// redis.zrevrange("key", 0, -1, std::back_inserter(result)); + /// // send command with *WITHSCORES* option: + /// std::vector> with_score; + /// redis.zrevrange("key", 0, -1, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param start Start rank. Inclusive and can be negative. + /// @param stop Stop rank. Inclusive and can be negative. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZREVRANGE key start stop* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZREVRANGE key start stop WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @see `Redis::zrange` + /// @see https://redis.io/commands/zrevrange + template + void zrevrange(const StringView &key, long long start, long long stop, Output output); + + /// @brief Get a range of members by lexicographical order (from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Get members between [abc, abd] in reverse order. + /// redis.zrevrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `Redis::zrangebylex` + /// @see https://redis.io/commands/zrevrangebylex + template + void zrevrangebylex(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by lexicographical order (from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Limit the result to at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members between [abc, abd] in reverse order. + /// redis.zrevrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// opts, std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `LimitOptions` + /// @see `Redis::zrangebylex` + /// @see https://redis.io/commands/zrevrangebylex + template + void zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get a range of members by score (ordered from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZREVRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Get members whose score between (3, 6) in reverse order. + /// redis.zrevrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf) in reverse order. + /// redis.zrevrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZREVRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZREVRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrangebyscore` + /// @see https://redis.io/commands/zrevrangebyscore + template + void zrevrangebyscore(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by score (ordered from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZREVRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Only return at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members whose score between (3, 6) in reverse order. + /// redis.zrevrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// opts, std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf) in reverse order. + /// redis.zrevrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// opts, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZREVRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZREVRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrangebyscore` + /// @see https://redis.io/commands/zrevrangebyscore + template + void zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get the rank (from high to low) of the given member in the sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member. + /// @return The rank of the given member. + /// @note If the member does not exist, `zrevrank` returns `OptionalLongLong{}` (`std::nullopt`). + /// @see https://redis.io/commands/zrevrank + OptionalLongLong zrevrank(const StringView &key, const StringView &member); + + /// @brief Scan members of the given sorted set matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::vector> members; + /// while (true) { + /// cursor = redis.zscan("zset", cursor, "pattern:*", + /// 10, std::back_inserter(members)); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of members to be scanned. + /// @param count A hint for how many members to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan members of the given sorted set matching the given pattern. + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of members to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan all members of the given sorted set. + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param count A hint for how many members to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + long long count, + Output output); + + /// @brief Scan all members of the given sorted set. + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + Output output); + + /// @brief Get the score of the given member. + /// @param key Key where the sorted set is stored. + /// @param member Member. + /// @return The score of the member. + /// @note If member does not exist, `zscore` returns `OptionalDouble{}` (`std::nullopt`). + /// @see https://redis.io/commands/zscore + OptionalDouble zscore(const StringView &key, const StringView &member); + + /// @brief Copy a sorted set to another one with the scores being multiplied by a factor. + /// @param destination Key of the destination sorted set. + /// @param key Key of the source sorted set. + /// @param weight Weight to be multiplied to the score of each member. + /// @return The number of members in the sorted set. + /// @note There's no aggregation type parameter for single key overload, since these 3 types + /// have the same effect. + /// @see `Redis::zinterstore` + /// @see https://redis.io/commands/zinterstore + long long zunionstore(const StringView &destination, const StringView &key, double weight); + + /// @brief Get union of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// std::vector keys = {"k1", "k2", "k3"}; + /// redis.zunionstore("destination", keys.begin(), keys.end()); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// std::vector> keys_with_weights = {{"k1", 1}, {"k2", 2}}; + /// redis.zunionstore("destination", keys_with_weights.begin(), + /// keys_with_weights.end(), Aggregation::MIN); + /// // NOTE: `keys_with_weights` can also be of type `std::unordered_map`. + /// // However, it will be slower than std::vector>, since we use + /// // `std::distance(first, last)` to calculate the *numkeys* parameter. + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param first Iterator to the first sorted set (might with weight). + /// @param last Off-the-end iterator to the sorted set range. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `Input` is an + /// iterator to a container of `std::string`, we use the default weight, i.e. 1, and send + /// *ZUNIONSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `Input` is an iterator to a container of `std::pair`, + /// i.e. key-weight pair, we send the command with the given weights: + /// *ZUNIONSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zinterstore` + /// @see https://redis.io/commands/zunionstore + template + long long zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + /// @brief Get union of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// redis.zunionstore("destination", {"k1", "k2"}); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// redis.zunionstore("destination", + /// {std::make_pair("k1", 1), std::make_pair("k2", 2)}, Aggregation::MIN); + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param il Initializer list of sorted set. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `T` is + /// of type `std::string`, we use the default weight, i.e. 1, and send + /// *ZUNIONSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `T` is of type `std::pair`, i.e. key-weight pair, + /// we send the command with the given weights: + /// *ZUNIONSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zinterstore` + /// @see https://redis.io/commands/zunionstore + template + long long zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + /// @brief Add the given element to a hyperloglog. + /// @param key Key of the hyperloglog. + /// @param element Element to be added. + /// @return Whether any of hyperloglog's internal register has been altered. + /// @retval true If at least one internal register has been altered. + /// @retval false If none of internal registers has been altered. + /// @note When `pfadd` returns false, it does not mean that this method failed to add + /// an element to the hyperloglog. Instead it means that the internal registers + /// were not altered. If `pfadd` fails, it will throw an exception of `Exception` type. + /// @see https://redis.io/commands/pfadd + bool pfadd(const StringView &key, const StringView &element); + + /// @brief Add the given elements to a hyperloglog. + /// @param key Key of the hyperloglog. + /// @param first Iterator to the first element. + /// @param last Off-the-end iterator to the given range. + /// @return Whether any of hyperloglog's internal register has been altered. + /// @retval true If at least one internal register has been altered. + /// @retval false If none of internal registers has been altered. + /// @note When `pfadd` returns false, it does not mean that this method failed to add + /// an element to the hyperloglog. Instead it means that the internal registers + /// were not altered. If `pfadd` fails, it will throw an exception of `Exception` type. + /// @see https://redis.io/commands/pfadd + template + bool pfadd(const StringView &key, Input first, Input last); + + /// @brief Add the given elements to a hyperloglog. + /// @param key Key of the hyperloglog. + /// @param il Initializer list of elements to be added. + /// @return Whether any of hyperloglog's internal register has been altered. + /// @retval true If at least one internal register has been altered. + /// @retval false If none of internal registers has been altered. + /// @note When `pfadd` returns false, it does not mean that this method failed to add + /// an element to the hyperloglog. Instead it means that the internal registers + /// were not altered. If `pfadd` fails, it will throw an exception of `Exception` type. + /// @see https://redis.io/commands/pfadd + template + bool pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + long long pfcount(const StringView &key); + + template + long long pfcount(Input first, Input last); + + template + long long pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + void pfmerge(const StringView &destination, const StringView &key); + + template + void pfmerge(const StringView &destination, Input first, Input last); + + template + void pfmerge(const StringView &destination, std::initializer_list il) { + pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + long long geoadd(const StringView &key, + const std::tuple &member); + + template + long long geoadd(const StringView &key, + Input first, + Input last); + + template + long long geoadd(const StringView &key, + std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + OptionalDouble geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M); + + OptionalString geohash(const StringView &key, const StringView &member); + + template + void geohash(const StringView &key, Input first, Input last, Output output); + + template + void geohash(const StringView &key, std::initializer_list il, Output output) { + geohash(key, il.begin(), il.end(), output); + } + + Optional> geopos(const StringView &key, const StringView &member); + + template + void geopos(const StringView &key, Input first, Input last, Output output); + + template + void geopos(const StringView &key, std::initializer_list il, Output output) { + geopos(key, il.begin(), il.end(), output); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + + /// @brief Get members in geo range, i.e. a circle, and store them in a sorted set. + /// @param key Key of the GEO set. + /// @param loc Location encoded with pair. + /// @param radius Radius of the range. + /// @param unit Radius unit. + /// @param destination Key of the destination sorted set. + /// @param store_dist Whether store distance info instead of geo info to destination. + /// @param count Limit the first N members. + /// @return Number of members stored in destination. + /// @note Before Redis 6.2.6, if key does not exist, returns `OptionalLongLong{}` (`std::nullopt`). + /// Since Redis 6.2.6, if key does not exist, returns 0. + /// @see `GeoUnit` + /// @see `Redis::georadiusbymember` + /// @see https://redis.io/commands/georadius + OptionalLongLong georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // If *output* is an iterator of a container of string, we send *GEORADIUS* command + // without any options and only get the members in the specified geo range. + // If *output* is an iterator of a container of a tuple, the type of the tuple decides + // options we send with the *GEORADIUS* command. If the tuple has an element of type + // double, we send the *WITHDIST* option. If it has an element of type string, we send + // the *WITHHASH* option. If it has an element of type pair, we send + // the *WITHCOORD* option. For example: + // + // The following code only gets the members in range, i.e. without any option. + // + // vector members; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(members)) + // + // The following code sends the command with *WITHDIST* option. + // + // vector> with_dist; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist)) + // + // The following code sends the command with *WITHDIST* and *WITHHASH* options. + // + // vector> with_dist_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_hash)) + // + // The following code sends the command with *WITHDIST*, *WITHCOORD* and *WITHHASH* options. + // + // vector, string>> with_dist_coord_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_coord_hash)) + // + // This also applies to *GEORADIUSBYMEMBER*. + template + void georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + /// @brief Get members in geo range, i.e. a circle, and store them in a sorted set. + /// @param key Key of the GEO set. + /// @param member Member which is the center of the circle. + /// @param radius Radius of the range. + /// @param unit Radius unit. + /// @param destination Key of the destination sorted set. + /// @param store_dist Whether store distance info instead of geo info to destination. + /// @param count Limit the first N members. + /// @return Number of members stored in destination. + /// @note Before Redis 6.2.6, if key does not exist, returns `OptionalLongLong{}` (`std::nullopt`). + /// Since Redis 6.2.6, if key does not exist, returns 0. + /// @note If member does not exist, throw an `ReplyError`. + /// @see `GeoUnit` + /// @see `Redis::georadius` + /// @see https://redis.io/commands/georadiusbymember + OptionalLongLong georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // See comments on *GEORADIUS*. + template + void georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + // SCRIPTING commands. + + template + Result eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + Result evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + /// @brief Check if the given script exists. + /// @param sha1 SHA1 digest of the script. + /// @return Whether the script exists. + /// @retval true If the script exists. + /// @retval false If the script does not exist. + /// @see https://redis.io/commands/script-exists + bool script_exists(const StringView &sha1); + + template + void script_exists(Input first, Input last, Output output); + + template + void script_exists(std::initializer_list il, Output output) { + script_exists(il.begin(), il.end(), output); + } + + void script_flush(); + + void script_kill(); + + std::string script_load(const StringView &script); + + // PUBSUB commands. + + long long publish(const StringView &channel, const StringView &message); + + // Transaction commands. + void watch(const StringView &key); + + template + void watch(Input first, Input last); + + template + void watch(std::initializer_list il) { + watch(il.begin(), il.end()); + } + + void unwatch(); + + // Stream commands. + + long long xack(const StringView &key, const StringView &group, const StringView &id); + + template + long long xack(const StringView &key, const StringView &group, Input first, Input last); + + template + long long xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, const StringView &id, Input first, Input last); + + template + std::string xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true); + + template + std::string xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il, + Output output) { + xclaim(key, group, consumer, min_idle_time, il.begin(), il.end(), output); + } + + long long xdel(const StringView &key, const StringView &id); + + template + long long xdel(const StringView &key, Input first, Input last); + + template + long long xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + void xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false); + + void xgroup_setid(const StringView &key, const StringView &group, const StringView &id); + + long long xgroup_destroy(const StringView &key, const StringView &group); + + long long xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer); + + long long xlen(const StringView &key); + + template + auto xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple; + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + Output output) { + xread(key, id, 0, output); + } + + template + auto xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, Input last, Output output) + -> typename std::enable_if::value>::type { + xread(first ,last, 0, output); + } + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xread(key, id, timeout, 0, output); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xread(first, last, timeout, 0, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + Output output) { + xreadgroup(group, consumer, key, id, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, 0, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, timeout, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xreadgroup(group, consumer, key, id, timeout, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, 0, false, output); + } + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output); + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output); + + long long xtrim(const StringView &key, long long count, bool approx = true); + +private: + template + friend class QueuedRedis; + + friend class RedisCluster; + + // For internal use. + explicit Redis(const GuardedConnectionSPtr &connection); + + template + ReplyUPtr _command(const StringView &cmd_name, const IndexSequence &, Args &&...args) { + return command(cmd_name, NthValue(std::forward(args)...)...); + } + + template + ReplyUPtr _command(Connection &connection, Cmd cmd, Args &&...args); + + template + ReplyUPtr _score_command(std::true_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(std::false_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(Cmd cmd, Args &&... args); + + // Pool Mode. + // Public constructors create a *Redis* instance with a pool. + // In this case, *_connection* is a null pointer, and is never used. + ConnectionPoolSPtr _pool; + + // Single Connection Mode. + // Private constructor creates a *Redis* instance with a single connection. + // This is used when we create Transaction, Pipeline and Subscriber. + // In this case, *_pool* is empty, and is never used. + GuardedConnectionSPtr _connection; +}; + +} + +} + +#include "redis.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.hpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.hpp new file mode 100644 index 000000000..c560ce111 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis.hpp @@ -0,0 +1,1342 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_HPP +#define SEWENEW_REDISPLUSPLUS_REDIS_HPP + +#include "command.h" +#include "reply.h" +#include "utils.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +template +auto Redis::command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (_connection) { + // Single Connection Mode. + // TODO: In this case, should we reconnect? + auto &connection = _connection->connection(); + if (connection.broken()) { + throw Error("Connection is broken"); + } + + return _command(connection, cmd, std::forward(args)...); + } else { + assert(_pool); + + // Pool Mode, i.e. get connection from pool. + SafeConnection connection(*_pool); + + return _command(connection.connection(), cmd, std::forward(args)...); + } +} + +template +auto Redis::command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, ReplyUPtr>::type { + auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, std::forward(args)...); + connection.send(cmd_args); + }; + + return command(cmd, cmd_name, std::forward(args)...); +} + +template +auto Redis::command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type { + range_check("command", first, last); + + auto cmd = [](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +Result Redis::command(const StringView &cmd_name, Args &&...args) { + auto r = command(cmd_name, std::forward(args)...); + + assert(r); + + return reply::parse(*r); +} + +template +auto Redis::command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, void>::type { + auto r = _command(cmd_name, + MakeIndexSequence(), + std::forward(args)...); + + assert(r); + + reply::to_array(*r, LastValue(std::forward(args)...)); +} + +template +auto Redis::command(Input first, Input last) + -> typename std::enable_if::value, Result>::type { + auto r = command(first, last); + + assert(r); + + return reply::parse(*r); +} + +template +auto Redis::command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type { + auto r = command(first, last); + + assert(r); + + reply::to_array(*r, output); +} + +// KEY commands. + +template +long long Redis::del(Input first, Input last) { + range_check("DEL", first, last); + + auto reply = command(cmd::del_range, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::exists(Input first, Input last) { + range_check("EXISTS", first, last); + + auto reply = command(cmd::exists_range, first, last); + + return reply::parse(*reply); +} + +inline bool Redis::expire(const StringView &key, const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); +} + +inline bool Redis::expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); +} + +template +void Redis::keys(const StringView &pattern, Output output) { + auto reply = command(cmd::keys, pattern); + + reply::to_array(*reply, output); +} + +inline bool Redis::pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); +} + +inline bool Redis::pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); +} + +inline void Redis::restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + bool replace) { + return restore(key, val, ttl.count(), replace); +} + +template +long long Redis::scan(long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::scan, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::scan(long long cursor, + const StringView &pattern, + Output output) { + return scan(cursor, pattern, 10, output); +} + +template +inline long long Redis::scan(long long cursor, + long long count, + Output output) { + return scan(cursor, "*", count, output); +} + +template +inline long long Redis::scan(long long cursor, + Output output) { + return scan(cursor, "*", 10, output); +} + +template +long long Redis::touch(Input first, Input last) { + range_check("TOUCH", first, last); + + auto reply = command(cmd::touch_range, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::unlink(Input first, Input last) { + range_check("UNLINK", first, last); + + auto reply = command(cmd::unlink_range, first, last); + + return reply::parse(*reply); +} + +inline long long Redis::wait(long long numslaves, const std::chrono::milliseconds &timeout) { + return wait(numslaves, timeout.count()); +} + +// STRING commands. + +template +long long Redis::bitop(BitOp op, const StringView &destination, Input first, Input last) { + range_check("BITOP", first, last); + + auto reply = command(cmd::bitop_range, op, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::mget(Input first, Input last, Output output) { + range_check("MGET", first, last); + + auto reply = command(cmd::mget, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::mset(Input first, Input last) { + range_check("MSET", first, last); + + auto reply = command(cmd::mset, first, last); + + reply::parse(*reply); +} + +template +bool Redis::msetnx(Input first, Input last) { + range_check("MSETNX", first, last); + + auto reply = command(cmd::msetnx, first, last); + + return reply::parse(*reply); +} + +inline void Redis::psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); +} + +inline void Redis::setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + setex(key, ttl.count(), val); +} + +// LIST commands. + +template +OptionalStringPair Redis::blpop(Input first, Input last, long long timeout) { + range_check("BLPOP", first, last); + + auto reply = command(cmd::blpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair Redis::blpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return blpop(first, last, timeout.count()); +} + +template +OptionalStringPair Redis::brpop(Input first, Input last, long long timeout) { + range_check("BRPOP", first, last); + + auto reply = command(cmd::brpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair Redis::brpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return brpop(first, last, timeout.count()); +} + +inline OptionalString Redis::brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return brpoplpush(source, destination, timeout.count()); +} + +template +inline long long Redis::lpush(const StringView &key, Input first, Input last) { + range_check("LPUSH", first, last); + + auto reply = command(cmd::lpush_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::lrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = command(cmd::lrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline long long Redis::rpush(const StringView &key, Input first, Input last) { + range_check("RPUSH", first, last); + + auto reply = command(cmd::rpush_range, key, first, last); + + return reply::parse(*reply); +} + +// HASH commands. + +template +inline long long Redis::hdel(const StringView &key, Input first, Input last) { + range_check("HDEL", first, last); + + auto reply = command(cmd::hdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::hgetall(const StringView &key, Output output) { + auto reply = command(cmd::hgetall, key); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hkeys(const StringView &key, Output output) { + auto reply = command(cmd::hkeys, key); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hmget(const StringView &key, Input first, Input last, Output output) { + range_check("HMGET", first, last); + + auto reply = command(cmd::hmget, key, first, last); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hmset(const StringView &key, Input first, Input last) { + range_check("HMSET", first, last); + + auto reply = command(cmd::hmset, key, first, last); + + reply::parse(*reply); +} + +template +long long Redis::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::hscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return hscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return hscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + Output output) { + return hscan(key, cursor, "*", 10, output); +} + +template +auto Redis::hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, + long long>::type { + range_check("HSET", first, last); + + auto reply = command(cmd::hset_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::hvals(const StringView &key, Output output) { + auto reply = command(cmd::hvals, key); + + reply::to_array(*reply, output); +} + +// SET commands. + +template +long long Redis::sadd(const StringView &key, Input first, Input last) { + range_check("SADD", first, last); + + auto reply = command(cmd::sadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void Redis::sdiff(Input first, Input last, Output output) { + range_check("SDIFF", first, last); + + auto reply = command(cmd::sdiff, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sdiffstore(const StringView &destination, + Input first, + Input last) { + range_check("SDIFFSTORE", first, last); + + auto reply = command(cmd::sdiffstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::sinter(Input first, Input last, Output output) { + range_check("SINTER", first, last); + + auto reply = command(cmd::sinter, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sinterstore(const StringView &destination, + Input first, + Input last) { + range_check("SINTERSTORE", first, last); + + auto reply = command(cmd::sinterstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::smembers(const StringView &key, Output output) { + auto reply = command(cmd::smembers, key); + + reply::to_array(*reply, output); +} + +template +void Redis::spop(const StringView &key, long long count, Output output) { + auto reply = command(cmd::spop_range, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::srandmember(const StringView &key, long long count, Output output) { + auto reply = command(cmd::srandmember_range, key, count); + + reply::to_array(*reply, output); +} + +template +long long Redis::srem(const StringView &key, Input first, Input last) { + range_check("SREM", first, last); + + auto reply = command(cmd::srem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::sscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return sscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return sscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + Output output) { + return sscan(key, cursor, "*", 10, output); +} + +template +void Redis::sunion(Input first, Input last, Output output) { + range_check("SUNION", first, last); + + auto reply = command(cmd::sunion, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sunionstore(const StringView &destination, Input first, Input last) { + range_check("SUNIONSTORE", first, last); + + auto reply = command(cmd::sunionstore_range, destination, first, last); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +inline auto Redis::bzpopmax(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(key, timeout.count()); +} + +template +auto Redis::bzpopmax(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto Redis::bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(first, last, timeout.count()); +} + +inline auto Redis::bzpopmin(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(key, timeout.count()); +} + +template +auto Redis::bzpopmin(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto Redis::bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(first, last, timeout.count()); +} + +template +long long Redis::zadd(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + range_check("ZADD", first, last); + + auto reply = command(cmd::zadd_range, key, first, last, type, changed); + + return reply::parse(*reply); +} + +template +long long Redis::zcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zcount, key, interval); + + return reply::parse(*reply); +} + +template +long long Redis::zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZINTERSTORE", first, last); + + auto reply = command(cmd::zinterstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +template +long long Redis::zlexcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zlexcount, key, interval); + + return reply::parse(*reply); +} + +template +void Redis::zpopmax(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmax, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::zpopmin(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmin, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::zrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +void Redis::zrangebylex(const StringView &key, const Interval &interval, Output output) { + zrangebylex(key, interval, {}, output); +} + +template +void Redis::zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void Redis::zrangebyscore(const StringView &key, + const Interval &interval, + Output output) { + zrangebyscore(key, interval, {}, output); +} + +template +void Redis::zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrangebyscore, + key, + interval, + opts); + + reply::to_array(*reply, output); +} + +template +long long Redis::zrem(const StringView &key, Input first, Input last) { + range_check("ZREM", first, last); + + auto reply = command(cmd::zrem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::zremrangebylex(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebylex, key, interval); + + return reply::parse(*reply); +} + +template +long long Redis::zremrangebyscore(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebyscore, key, interval); + + return reply::parse(*reply); +} + +template +void Redis::zrevrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrevrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline void Redis::zrevrangebylex(const StringView &key, + const Interval &interval, + Output output) { + zrevrangebylex(key, interval, {}, output); +} + +template +void Redis::zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrevrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void Redis::zrevrangebyscore(const StringView &key, const Interval &interval, Output output) { + zrevrangebyscore(key, interval, {}, output); +} + +template +void Redis::zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrevrangebyscore, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +long long Redis::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::zscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return zscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return zscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + Output output) { + return zscan(key, cursor, "*", 10, output); +} + +template +long long Redis::zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZUNIONSTORE", first, last); + + auto reply = command(cmd::zunionstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +template +bool Redis::pfadd(const StringView &key, Input first, Input last) { + range_check("PFADD", first, last); + + auto reply = command(cmd::pfadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::pfcount(Input first, Input last) { + range_check("PFCOUNT", first, last); + + auto reply = command(cmd::pfcount_range, first, last); + + return reply::parse(*reply); +} + +template +void Redis::pfmerge(const StringView &destination, + Input first, + Input last) { + range_check("PFMERGE", first, last); + + auto reply = command(cmd::pfmerge_range, destination, first, last); + + reply::parse(*reply); +} + +// GEO commands. + +template +inline long long Redis::geoadd(const StringView &key, + Input first, + Input last) { + range_check("GEOADD", first, last); + + auto reply = command(cmd::geoadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void Redis::geohash(const StringView &key, Input first, Input last, Output output) { + range_check("GEOHASH", first, last); + + auto reply = command(cmd::geohash_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::geopos(const StringView &key, Input first, Input last, Output output) { + range_check("GEOPOS", first, last); + + auto reply = command(cmd::geopos_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +template +void Redis::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +// SCRIPTING commands. + +template +Result Redis::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + auto reply = command(cmd::eval, script, keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result Redis::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return eval(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void Redis::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + auto reply = command(cmd::eval, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void Redis::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + eval(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +template +Result Redis::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + auto reply = command(cmd::evalsha, script, + keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result Redis::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return evalsha(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void Redis::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + auto reply = command(cmd::evalsha, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void Redis::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + evalsha(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +template +void Redis::script_exists(Input first, Input last, Output output) { + range_check("SCRIPT EXISTS", first, last); + + auto reply = command(cmd::script_exists_range, first, last); + + reply::to_array(*reply, output); +} + +// Transaction commands. + +template +void Redis::watch(Input first, Input last) { + auto reply = command(cmd::watch_range, first, last); + + reply::parse(*reply); +} + +// Stream commands. + +template +long long Redis::xack(const StringView &key, const StringView &group, Input first, Input last) { + auto reply = command(cmd::xack_range, key, group, first, last); + + return reply::parse(*reply); +} + +template +std::string Redis::xadd(const StringView &key, const StringView &id, Input first, Input last) { + auto reply = command(cmd::xadd_range, key, id, first, last); + + return reply::parse(*reply); +} + +template +std::string Redis::xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + auto reply = command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + + return reply::parse(*reply); +} + +template +void Redis::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output) { + auto reply = command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + + reply::to_array(*reply, output); +} + +template +void Redis::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output) { + auto reply = command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + + reply::to_array(*reply, output); +} + +template +long long Redis::xdel(const StringView &key, Input first, Input last) { + auto reply = command(cmd::xdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +auto Redis::xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple { + auto reply = command(cmd::xpending, key, group); + + return reply::parse_xpending_reply(*reply, output); +} + +template +void Redis::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xpending_detail, key, group, start, end, count); + + reply::to_array(*reply, output); +} + +template +void Redis::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output) { + auto reply = command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + + reply::to_array(*reply, output); +} + +template +void Redis::xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output) { + auto reply = command(cmd::xrange, key, start, end); + + reply::to_array(*reply, output); +} + +template +void Redis::xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xrange_count, key, start, end, count); + + reply::to_array(*reply, output); +} + +template +void Redis::xread(const StringView &key, + const StringView &id, + long long count, + Output output) { + auto reply = command(cmd::xread, key, id, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_range, first, last, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + auto reply = command(cmd::xread_block, key, id, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_block_range, first, last, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output) { + auto reply = command(cmd::xreadgroup, group, consumer, key, id, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = command(cmd::xreadgroup_range, group, consumer, first, last, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) { + auto reply = command(cmd::xreadgroup_block, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = command(cmd::xreadgroup_block_range, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output) { + auto reply = command(cmd::xrevrange, key, end, start); + + reply::to_array(*reply, output); +} + +template +void Redis::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output) { + auto reply = command(cmd::xrevrange_count, key, end, start, count); + + reply::to_array(*reply, output); +} + +template +ReplyUPtr Redis::_command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + + auto reply = connection.recv(); + + return reply; +} + +template +inline ReplyUPtr Redis::_score_command(std::true_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., true); +} + +template +inline ReplyUPtr Redis::_score_command(std::false_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., false); +} + +template +inline ReplyUPtr Redis::_score_command(Cmd cmd, Args &&... args) { + return _score_command(typename IsKvPairIter::type(), + cmd, + std::forward(args)...); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_HPP diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.cpp new file mode 100644 index 000000000..118414b00 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.cpp @@ -0,0 +1,802 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "redis_cluster.h" +#include +#include "command.h" +#include "errors.h" +#include "queued_redis.h" + +namespace sw { + +namespace redis { + +RedisCluster::RedisCluster(const std::string &uri) : RedisCluster(ConnectionOptions(uri)) {} + +Redis RedisCluster::redis(const StringView &hash_tag, bool new_connection) { + auto pool = _pool.fetch(hash_tag); + if (new_connection) { + // Create a new pool + pool = std::make_shared(pool->clone()); + } + + return Redis(std::make_shared(pool)); +} + +Pipeline RedisCluster::pipeline(const StringView &hash_tag, bool new_connection) { + auto pool = _pool.fetch(hash_tag); + if (new_connection) { + // Create a new pool + pool = std::make_shared(pool->clone()); + } + + return Pipeline(pool, new_connection); +} + +Transaction RedisCluster::transaction(const StringView &hash_tag, bool piped, bool new_connection) { + auto pool = _pool.fetch(hash_tag); + if (new_connection) { + // Create a new pool + pool = std::make_shared(pool->clone()); + } + + return Transaction(pool, new_connection, piped); +} + +Subscriber RedisCluster::subscriber() { + auto opts = _pool.connection_options(); + return Subscriber(Connection(opts)); +} + +// KEY commands. + +long long RedisCluster::del(const StringView &key) { + auto reply = command(cmd::del, key); + + return reply::parse(*reply); +} + +OptionalString RedisCluster::dump(const StringView &key) { + auto reply = command(cmd::dump, key); + + return reply::parse(*reply); +} + +long long RedisCluster::exists(const StringView &key) { + auto reply = command(cmd::exists, key); + + return reply::parse(*reply); +} + +bool RedisCluster::expire(const StringView &key, long long timeout) { + auto reply = command(cmd::expire, key, timeout); + + return reply::parse(*reply); +} + +bool RedisCluster::expireat(const StringView &key, long long timestamp) { + auto reply = command(cmd::expireat, key, timestamp); + + return reply::parse(*reply); +} + +bool RedisCluster::persist(const StringView &key) { + auto reply = command(cmd::persist, key); + + return reply::parse(*reply); +} + +bool RedisCluster::pexpire(const StringView &key, long long timeout) { + auto reply = command(cmd::pexpire, key, timeout); + + return reply::parse(*reply); +} + +bool RedisCluster::pexpireat(const StringView &key, long long timestamp) { + auto reply = command(cmd::pexpireat, key, timestamp); + + return reply::parse(*reply); +} + +long long RedisCluster::pttl(const StringView &key) { + auto reply = command(cmd::pttl, key); + + return reply::parse(*reply); +} + +void RedisCluster::rename(const StringView &key, const StringView &newkey) { + auto reply = command(cmd::rename, key, newkey); + + reply::parse(*reply); +} + +bool RedisCluster::renamenx(const StringView &key, const StringView &newkey) { + auto reply = command(cmd::renamenx, key, newkey); + + return reply::parse(*reply); +} + +void RedisCluster::restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace) { + auto reply = command(cmd::restore, key, val, ttl, replace); + + reply::parse(*reply); +} + +long long RedisCluster::touch(const StringView &key) { + auto reply = command(cmd::touch, key); + + return reply::parse(*reply); +} + +long long RedisCluster::ttl(const StringView &key) { + auto reply = command(cmd::ttl, key); + + return reply::parse(*reply); +} + +std::string RedisCluster::type(const StringView &key) { + auto reply = command(cmd::type, key); + + return reply::parse(*reply); +} + +long long RedisCluster::unlink(const StringView &key) { + auto reply = command(cmd::unlink, key); + + return reply::parse(*reply); +} + +// STRING commands. + +long long RedisCluster::append(const StringView &key, const StringView &val) { + auto reply = command(cmd::append, key, val); + + return reply::parse(*reply); +} + +long long RedisCluster::bitcount(const StringView &key, long long start, long long end) { + auto reply = command(cmd::bitcount, key, start, end); + + return reply::parse(*reply); +} + +long long RedisCluster::bitop(BitOp op, const StringView &destination, const StringView &key) { + auto reply = _command(cmd::bitop, destination, op, destination, key); + + return reply::parse(*reply); +} + +long long RedisCluster::bitpos(const StringView &key, + long long bit, + long long start, + long long end) { + auto reply = command(cmd::bitpos, key, bit, start, end); + + return reply::parse(*reply); +} + +long long RedisCluster::decr(const StringView &key) { + auto reply = command(cmd::decr, key); + + return reply::parse(*reply); +} + +long long RedisCluster::decrby(const StringView &key, long long decrement) { + auto reply = command(cmd::decrby, key, decrement); + + return reply::parse(*reply); +} + +OptionalString RedisCluster::get(const StringView &key) { + auto reply = command(cmd::get, key); + + return reply::parse(*reply); +} + +long long RedisCluster::getbit(const StringView &key, long long offset) { + auto reply = command(cmd::getbit, key, offset); + + return reply::parse(*reply); +} + +std::string RedisCluster::getrange(const StringView &key, long long start, long long end) { + auto reply = command(cmd::getrange, key, start, end); + + return reply::parse(*reply); +} + +OptionalString RedisCluster::getset(const StringView &key, const StringView &val) { + auto reply = command(cmd::getset, key, val); + + return reply::parse(*reply); +} + +long long RedisCluster::incr(const StringView &key) { + auto reply = command(cmd::incr, key); + + return reply::parse(*reply); +} + +long long RedisCluster::incrby(const StringView &key, long long increment) { + auto reply = command(cmd::incrby, key, increment); + + return reply::parse(*reply); +} + +double RedisCluster::incrbyfloat(const StringView &key, double increment) { + auto reply = command(cmd::incrbyfloat, key, increment); + + return reply::parse(*reply); +} + +void RedisCluster::psetex(const StringView &key, + long long ttl, + const StringView &val) { + auto reply = command(cmd::psetex, key, ttl, val); + + reply::parse(*reply); +} + +bool RedisCluster::set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + UpdateType type) { + auto reply = command(cmd::set, key, val, ttl.count(), type); + + reply::rewrite_set_reply(*reply); + + return reply::parse(*reply); +} + +void RedisCluster::setex(const StringView &key, + long long ttl, + const StringView &val) { + auto reply = command(cmd::setex, key, ttl, val); + + reply::parse(*reply); +} + +bool RedisCluster::setnx(const StringView &key, const StringView &val) { + auto reply = command(cmd::setnx, key, val); + + return reply::parse(*reply); +} + +long long RedisCluster::setrange(const StringView &key, long long offset, const StringView &val) { + auto reply = command(cmd::setrange, key, offset, val); + + return reply::parse(*reply); +} + +long long RedisCluster::strlen(const StringView &key) { + auto reply = command(cmd::strlen, key); + + return reply::parse(*reply); +} + +// LIST commands. + +OptionalStringPair RedisCluster::blpop(const StringView &key, long long timeout) { + auto reply = command(cmd::blpop, key, timeout); + + return reply::parse(*reply); +} + +OptionalStringPair RedisCluster::blpop(const StringView &key, const std::chrono::seconds &timeout) { + return blpop(key, timeout.count()); +} + +OptionalStringPair RedisCluster::brpop(const StringView &key, long long timeout) { + auto reply = command(cmd::brpop, key, timeout); + + return reply::parse(*reply); +} + +OptionalStringPair RedisCluster::brpop(const StringView &key, const std::chrono::seconds &timeout) { + return brpop(key, timeout.count()); +} + +OptionalString RedisCluster::brpoplpush(const StringView &source, + const StringView &destination, + long long timeout) { + auto reply = command(cmd::brpoplpush, source, destination, timeout); + + return reply::parse(*reply); +} + +OptionalString RedisCluster::lindex(const StringView &key, long long index) { + auto reply = command(cmd::lindex, key, index); + + return reply::parse(*reply); +} + +long long RedisCluster::linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val) { + auto reply = command(cmd::linsert, key, position, pivot, val); + + return reply::parse(*reply); +} + +long long RedisCluster::llen(const StringView &key) { + auto reply = command(cmd::llen, key); + + return reply::parse(*reply); +} + +OptionalString RedisCluster::lpop(const StringView &key) { + auto reply = command(cmd::lpop, key); + + return reply::parse(*reply); +} + +long long RedisCluster::lpush(const StringView &key, const StringView &val) { + auto reply = command(cmd::lpush, key, val); + + return reply::parse(*reply); +} + +long long RedisCluster::lpushx(const StringView &key, const StringView &val) { + auto reply = command(cmd::lpushx, key, val); + + return reply::parse(*reply); +} + +long long RedisCluster::lrem(const StringView &key, long long count, const StringView &val) { + auto reply = command(cmd::lrem, key, count, val); + + return reply::parse(*reply); +} + +void RedisCluster::lset(const StringView &key, long long index, const StringView &val) { + auto reply = command(cmd::lset, key, index, val); + + reply::parse(*reply); +} + +void RedisCluster::ltrim(const StringView &key, long long start, long long stop) { + auto reply = command(cmd::ltrim, key, start, stop); + + reply::parse(*reply); +} + +OptionalString RedisCluster::rpop(const StringView &key) { + auto reply = command(cmd::rpop, key); + + return reply::parse(*reply); +} + +OptionalString RedisCluster::rpoplpush(const StringView &source, const StringView &destination) { + auto reply = command(cmd::rpoplpush, source, destination); + + return reply::parse(*reply); +} + +long long RedisCluster::rpush(const StringView &key, const StringView &val) { + auto reply = command(cmd::rpush, key, val); + + return reply::parse(*reply); +} + +long long RedisCluster::rpushx(const StringView &key, const StringView &val) { + auto reply = command(cmd::rpushx, key, val); + + return reply::parse(*reply); +} + +long long RedisCluster::hdel(const StringView &key, const StringView &field) { + auto reply = command(cmd::hdel, key, field); + + return reply::parse(*reply); +} + +bool RedisCluster::hexists(const StringView &key, const StringView &field) { + auto reply = command(cmd::hexists, key, field); + + return reply::parse(*reply); +} + +OptionalString RedisCluster::hget(const StringView &key, const StringView &field) { + auto reply = command(cmd::hget, key, field); + + return reply::parse(*reply); +} + +long long RedisCluster::hincrby(const StringView &key, const StringView &field, long long increment) { + auto reply = command(cmd::hincrby, key, field, increment); + + return reply::parse(*reply); +} + +double RedisCluster::hincrbyfloat(const StringView &key, const StringView &field, double increment) { + auto reply = command(cmd::hincrbyfloat, key, field, increment); + + return reply::parse(*reply); +} + +long long RedisCluster::hlen(const StringView &key) { + auto reply = command(cmd::hlen, key); + + return reply::parse(*reply); +} + +bool RedisCluster::hset(const StringView &key, const StringView &field, const StringView &val) { + auto reply = command(cmd::hset, key, field, val); + + return reply::parse(*reply); +} + +bool RedisCluster::hset(const StringView &key, const std::pair &item) { + return hset(key, item.first, item.second); +} + +bool RedisCluster::hsetnx(const StringView &key, const StringView &field, const StringView &val) { + auto reply = command(cmd::hsetnx, key, field, val); + + return reply::parse(*reply); +} + +bool RedisCluster::hsetnx(const StringView &key, const std::pair &item) { + return hsetnx(key, item.first, item.second); +} + +long long RedisCluster::hstrlen(const StringView &key, const StringView &field) { + auto reply = command(cmd::hstrlen, key, field); + + return reply::parse(*reply); +} + +// SET commands. + +long long RedisCluster::sadd(const StringView &key, const StringView &member) { + auto reply = command(cmd::sadd, key, member); + + return reply::parse(*reply); +} + +long long RedisCluster::scard(const StringView &key) { + auto reply = command(cmd::scard, key); + + return reply::parse(*reply); +} + +long long RedisCluster::sdiffstore(const StringView &destination, const StringView &key) { + auto reply = command(cmd::sdiffstore, destination, key); + + return reply::parse(*reply); +} + +long long RedisCluster::sinterstore(const StringView &destination, const StringView &key) { + auto reply = command(cmd::sinterstore, destination, key); + + return reply::parse(*reply); +} + +bool RedisCluster::sismember(const StringView &key, const StringView &member) { + auto reply = command(cmd::sismember, key, member); + + return reply::parse(*reply); +} + +bool RedisCluster::smove(const StringView &source, + const StringView &destination, + const StringView &member) { + auto reply = command(cmd::smove, source, destination, member); + + return reply::parse(*reply); +} + +OptionalString RedisCluster::spop(const StringView &key) { + auto reply = command(cmd::spop, key); + + return reply::parse(*reply); +} + +OptionalString RedisCluster::srandmember(const StringView &key) { + auto reply = command(cmd::srandmember, key); + + return reply::parse(*reply); +} + +long long RedisCluster::srem(const StringView &key, const StringView &member) { + auto reply = command(cmd::srem, key, member); + + return reply::parse(*reply); +} + +long long RedisCluster::sunionstore(const StringView &destination, const StringView &key) { + auto reply = command(cmd::sunionstore, destination, key); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +auto RedisCluster::bzpopmax(const StringView &key, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax, key, timeout); + + return reply::parse>>(*reply); +} + +auto RedisCluster::bzpopmin(const StringView &key, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin, key, timeout); + + return reply::parse>>(*reply); +} + +long long RedisCluster::zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type, + bool changed) { + auto reply = command(cmd::zadd, key, member, score, type, changed); + + return reply::parse(*reply); +} + +long long RedisCluster::zcard(const StringView &key) { + auto reply = command(cmd::zcard, key); + + return reply::parse(*reply); +} + +double RedisCluster::zincrby(const StringView &key, double increment, const StringView &member) { + auto reply = command(cmd::zincrby, key, increment, member); + + return reply::parse(*reply); +} + +long long RedisCluster::zinterstore(const StringView &destination, + const StringView &key, + double weight) { + auto reply = command(cmd::zinterstore, destination, key, weight); + + return reply::parse(*reply); +} + +Optional> RedisCluster::zpopmax(const StringView &key) { + auto reply = command(cmd::zpopmax, key, 1); + + reply::rewrite_empty_array_reply(*reply); + + return reply::parse>>(*reply); +} + +Optional> RedisCluster::zpopmin(const StringView &key) { + auto reply = command(cmd::zpopmin, key, 1); + + reply::rewrite_empty_array_reply(*reply); + + return reply::parse>>(*reply); +} + +OptionalLongLong RedisCluster::zrank(const StringView &key, const StringView &member) { + auto reply = command(cmd::zrank, key, member); + + return reply::parse(*reply); +} + +long long RedisCluster::zrem(const StringView &key, const StringView &member) { + auto reply = command(cmd::zrem, key, member); + + return reply::parse(*reply); +} + +long long RedisCluster::zremrangebyrank(const StringView &key, long long start, long long stop) { + auto reply = command(cmd::zremrangebyrank, key, start, stop); + + return reply::parse(*reply); +} + +OptionalLongLong RedisCluster::zrevrank(const StringView &key, const StringView &member) { + auto reply = command(cmd::zrevrank, key, member); + + return reply::parse(*reply); +} + +OptionalDouble RedisCluster::zscore(const StringView &key, const StringView &member) { + auto reply = command(cmd::zscore, key, member); + + return reply::parse(*reply); +} + +long long RedisCluster::zunionstore(const StringView &destination, + const StringView &key, + double weight) { + auto reply = command(cmd::zunionstore, destination, key, weight); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +bool RedisCluster::pfadd(const StringView &key, const StringView &element) { + auto reply = command(cmd::pfadd, key, element); + + return reply::parse(*reply); +} + +long long RedisCluster::pfcount(const StringView &key) { + auto reply = command(cmd::pfcount, key); + + return reply::parse(*reply); +} + +void RedisCluster::pfmerge(const StringView &destination, const StringView &key) { + auto reply = command(cmd::pfmerge, destination, key); + + reply::parse(*reply); +} + +// GEO commands. + +long long RedisCluster::geoadd(const StringView &key, + const std::tuple &member) { + auto reply = command(cmd::geoadd, key, member); + + return reply::parse(*reply); +} + +OptionalDouble RedisCluster::geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit) { + auto reply = command(cmd::geodist, key, member1, member2, unit); + + return reply::parse(*reply); +} + +OptionalString RedisCluster::geohash(const StringView &key, const StringView &member) { + auto reply = command(cmd::geohash, key, member); + + return reply::parse_leniently(*reply); +} + +Optional> RedisCluster::geopos(const StringView &key, + const StringView &member) { + auto reply = command(cmd::geopos, key, member); + + return reply::parse_leniently>>(*reply); +} + + +OptionalLongLong RedisCluster::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + auto reply = command(cmd::georadius_store, + key, + loc, + radius, + unit, + destination, + store_dist, + count); + + reply::rewrite_empty_array_reply(*reply); + + return reply::parse(*reply); +} + +OptionalLongLong RedisCluster::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + auto reply = command(cmd::georadiusbymember_store, + key, + member, + radius, + unit, + destination, + store_dist, + count); + + reply::rewrite_empty_array_reply(*reply); + + return reply::parse(*reply); +} + +// PUBSUB commands. + +long long RedisCluster::publish(const StringView &channel, const StringView &message) { + auto reply = command(cmd::publish, channel, message); + + return reply::parse(*reply); +} + +// Stream commands. + +long long RedisCluster::xack(const StringView &key, const StringView &group, const StringView &id) { + auto reply = command(cmd::xack, key, group, id); + + return reply::parse(*reply); +} + +long long RedisCluster::xdel(const StringView &key, const StringView &id) { + auto reply = command(cmd::xdel, key, id); + + return reply::parse(*reply); +} + +void RedisCluster::xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream) { + auto reply = command(cmd::xgroup_create, key, group, id, mkstream); + + reply::parse(*reply); +} + +void RedisCluster::xgroup_setid(const StringView &key, + const StringView &group, + const StringView &id) { + auto reply = command(cmd::xgroup_setid, key, group, id); + + reply::parse(*reply); +} + +long long RedisCluster::xgroup_destroy(const StringView &key, const StringView &group) { + auto reply = command(cmd::xgroup_destroy, key, group); + + return reply::parse(*reply); +} + +long long RedisCluster::xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer) { + auto reply = command(cmd::xgroup_delconsumer, key, group, consumer); + + return reply::parse(*reply); +} + +long long RedisCluster::xlen(const StringView &key) { + auto reply = command(cmd::xlen, key); + + return reply::parse(*reply); +} + +long long RedisCluster::xtrim(const StringView &key, long long count, bool approx) { + auto reply = command(cmd::xtrim, key, count, approx); + + return reply::parse(*reply); +} + +void RedisCluster::_asking(Connection &connection) { + // Send ASKING command. + connection.send("ASKING"); + + auto reply = connection.recv(); + + assert(reply); + + reply::parse(*reply); +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.h new file mode 100644 index 000000000..63c6131c1 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.h @@ -0,0 +1,1439 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H +#define SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H + +#include +#include +#include +#include +#include "shards_pool.h" +#include "reply.h" +#include "command_options.h" +#include "utils.h" +#include "subscriber.h" +#include "pipeline.h" +#include "transaction.h" +#include "redis.h" +#include "connection.h" + +namespace sw { + +namespace redis { + +template +class QueuedRedis; + +using Transaction = QueuedRedis; + +using Pipeline = QueuedRedis; + +class RedisCluster { +public: + RedisCluster(const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}, + Role role = Role::MASTER) : _pool(pool_opts, connection_opts, role) {} + + // Construct RedisCluster with URI: + // "tcp://127.0.0.1" or "tcp://127.0.0.1:6379" + // Only need to specify one URI. + explicit RedisCluster(const std::string &uri); + + RedisCluster(const RedisCluster &) = delete; + RedisCluster& operator=(const RedisCluster &) = delete; + + RedisCluster(RedisCluster &&) = default; + RedisCluster& operator=(RedisCluster &&) = default; + + Redis redis(const StringView &hash_tag, bool new_connection = true); + + Pipeline pipeline(const StringView &hash_tag, bool new_connection = true); + + Transaction transaction(const StringView &hash_tag, bool piped = false, bool new_connection = true); + + Subscriber subscriber(); + + template + auto command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && !IsIter::type>::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && IsIter::type>::value, void>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if::value + || std::is_arithmetic::type>::value, Result>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Result>::type; + + template + auto command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type; + + // KEY commands. + + long long del(const StringView &key); + + template + long long del(Input first, Input last); + + template + long long del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + OptionalString dump(const StringView &key); + + long long exists(const StringView &key); + + template + long long exists(Input first, Input last); + + template + long long exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + bool expire(const StringView &key, long long timeout); + + bool expire(const StringView &key, const std::chrono::seconds &timeout); + + bool expireat(const StringView &key, long long timestamp); + + bool expireat(const StringView &key, + const std::chrono::time_point &tp); + + bool persist(const StringView &key); + + bool pexpire(const StringView &key, long long timeout); + + bool pexpire(const StringView &key, const std::chrono::milliseconds &timeout); + + bool pexpireat(const StringView &key, long long timestamp); + + bool pexpireat(const StringView &key, + const std::chrono::time_point &tp); + + long long pttl(const StringView &key); + + void rename(const StringView &key, const StringView &newkey); + + bool renamenx(const StringView &key, const StringView &newkey); + + void restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false); + + void restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false); + + // TODO: sort + + long long touch(const StringView &key); + + template + long long touch(Input first, Input last); + + template + long long touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + long long ttl(const StringView &key); + + std::string type(const StringView &key); + + long long unlink(const StringView &key); + + template + long long unlink(Input first, Input last); + + template + long long unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + // STRING commands. + + long long append(const StringView &key, const StringView &str); + + long long bitcount(const StringView &key, long long start = 0, long long end = -1); + + long long bitop(BitOp op, const StringView &destination, const StringView &key); + + template + long long bitop(BitOp op, const StringView &destination, Input first, Input last); + + template + long long bitop(BitOp op, const StringView &destination, std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + long long bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1); + + long long decr(const StringView &key); + + long long decrby(const StringView &key, long long decrement); + + OptionalString get(const StringView &key); + + long long getbit(const StringView &key, long long offset); + + std::string getrange(const StringView &key, long long start, long long end); + + OptionalString getset(const StringView &key, const StringView &val); + + long long incr(const StringView &key); + + long long incrby(const StringView &key, long long increment); + + double incrbyfloat(const StringView &key, double increment); + + template + void mget(Input first, Input last, Output output); + + template + void mget(std::initializer_list il, Output output) { + mget(il.begin(), il.end(), output); + } + + template + void mset(Input first, Input last); + + template + void mset(std::initializer_list il) { + mset(il.begin(), il.end()); + } + + template + bool msetnx(Input first, Input last); + + template + bool msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + void psetex(const StringView &key, + long long ttl, + const StringView &val); + + void psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val); + + bool set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS); + + void setex(const StringView &key, + long long ttl, + const StringView &val); + + void setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val); + + bool setnx(const StringView &key, const StringView &val); + + long long setrange(const StringView &key, long long offset, const StringView &val); + + long long strlen(const StringView &key); + + // LIST commands. + + OptionalStringPair blpop(const StringView &key, long long timeout); + + OptionalStringPair blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(Input first, Input last, long long timeout); + + template + OptionalStringPair blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + OptionalStringPair brpop(const StringView &key, long long timeout); + + OptionalStringPair brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(Input first, Input last, long long timeout); + + template + OptionalStringPair brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + long long timeout); + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + OptionalString lindex(const StringView &key, long long index); + + long long linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + + long long llen(const StringView &key); + + OptionalString lpop(const StringView &key); + + long long lpush(const StringView &key, const StringView &val); + + template + long long lpush(const StringView &key, Input first, Input last); + + template + long long lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + long long lpushx(const StringView &key, const StringView &val); + + template + void lrange(const StringView &key, long long start, long long stop, Output output); + + long long lrem(const StringView &key, long long count, const StringView &val); + + void lset(const StringView &key, long long index, const StringView &val); + + void ltrim(const StringView &key, long long start, long long stop); + + OptionalString rpop(const StringView &key); + + OptionalString rpoplpush(const StringView &source, const StringView &destination); + + long long rpush(const StringView &key, const StringView &val); + + template + long long rpush(const StringView &key, Input first, Input last); + + template + long long rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + long long rpushx(const StringView &key, const StringView &val); + + // HASH commands. + + long long hdel(const StringView &key, const StringView &field); + + template + long long hdel(const StringView &key, Input first, Input last); + + template + long long hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + bool hexists(const StringView &key, const StringView &field); + + OptionalString hget(const StringView &key, const StringView &field); + + template + void hgetall(const StringView &key, Output output); + + long long hincrby(const StringView &key, const StringView &field, long long increment); + + double hincrbyfloat(const StringView &key, const StringView &field, double increment); + + template + void hkeys(const StringView &key, Output output); + + long long hlen(const StringView &key); + + template + void hmget(const StringView &key, Input first, Input last, Output output); + + template + void hmget(const StringView &key, std::initializer_list il, Output output) { + hmget(key, il.begin(), il.end(), output); + } + + template + void hmset(const StringView &key, Input first, Input last); + + template + void hmset(const StringView &key, std::initializer_list il) { + hmset(key, il.begin(), il.end()); + } + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + Output output); + + bool hset(const StringView &key, const StringView &field, const StringView &val); + + bool hset(const StringView &key, const std::pair &item); + + template + auto hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, long long>::type; + + template + long long hset(const StringView &key, std::initializer_list il) { + return hset(key, il.begin(), il.end()); + } + + bool hsetnx(const StringView &key, const StringView &field, const StringView &val); + + bool hsetnx(const StringView &key, const std::pair &item); + + long long hstrlen(const StringView &key, const StringView &field); + + template + void hvals(const StringView &key, Output output); + + // SET commands. + + long long sadd(const StringView &key, const StringView &member); + + template + long long sadd(const StringView &key, Input first, Input last); + + template + long long sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + long long scard(const StringView &key); + + template + void sdiff(Input first, Input last, Output output); + + template + void sdiff(std::initializer_list il, Output output) { + sdiff(il.begin(), il.end(), output); + } + + long long sdiffstore(const StringView &destination, const StringView &key); + + template + long long sdiffstore(const StringView &destination, + Input first, + Input last); + + template + long long sdiffstore(const StringView &destination, + std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + void sinter(Input first, Input last, Output output); + + template + void sinter(std::initializer_list il, Output output) { + sinter(il.begin(), il.end(), output); + } + + long long sinterstore(const StringView &destination, const StringView &key); + + template + long long sinterstore(const StringView &destination, + Input first, + Input last); + + template + long long sinterstore(const StringView &destination, + std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + bool sismember(const StringView &key, const StringView &member); + + template + void smembers(const StringView &key, Output output); + + bool smove(const StringView &source, + const StringView &destination, + const StringView &member); + + OptionalString spop(const StringView &key); + + template + void spop(const StringView &key, long long count, Output output); + + OptionalString srandmember(const StringView &key); + + template + void srandmember(const StringView &key, long long count, Output output); + + long long srem(const StringView &key, const StringView &member); + + template + long long srem(const StringView &key, Input first, Input last); + + template + long long srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + Output output); + + template + void sunion(Input first, Input last, Output output); + + template + void sunion(std::initializer_list il, Output output) { + sunion(il.begin(), il.end(), output); + } + + long long sunionstore(const StringView &destination, const StringView &key); + + template + long long sunionstore(const StringView &destination, Input first, Input last); + + template + long long sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + auto bzpopmax(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + auto bzpopmin(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + long long zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + long long zcard(const StringView &key); + + template + long long zcount(const StringView &key, const Interval &interval); + + double zincrby(const StringView &key, double increment, const StringView &member); + + long long zinterstore(const StringView &destination, const StringView &key, double weight); + + template + long long zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + long long zlexcount(const StringView &key, const Interval &interval); + + Optional> zpopmax(const StringView &key); + + template + void zpopmax(const StringView &key, long long count, Output output); + + Optional> zpopmin(const StringView &key); + + template + void zpopmin(const StringView &key, long long count, Output output); + + // If *output* is an iterator of a container of string, + // we send *ZRANGE key start stop* command. + // If it's an iterator of a container of pair, + // we send *ZRANGE key start stop WITHSCORES* command. + // + // The following code sends *ZRANGE* without the *WITHSCORES* option: + // + // vector result; + // redis.zrange("key", 0, -1, back_inserter(result)); + // + // On the other hand, the following code sends command with *WITHSCORES* option: + // + // unordered_map with_score; + // redis.zrange("key", 0, -1, inserter(with_score, with_score.end())); + // + // This also applies to other commands with the *WITHSCORES* option, + // e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, *ZREVRANGEBYSCORE*. + template + void zrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrank(const StringView &key, const StringView &member); + + long long zrem(const StringView &key, const StringView &member); + + template + long long zrem(const StringView &key, Input first, Input last); + + template + long long zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + long long zremrangebylex(const StringView &key, const Interval &interval); + + long long zremrangebyrank(const StringView &key, long long start, long long stop); + + template + long long zremrangebyscore(const StringView &key, const Interval &interval); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrevrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrevrank(const StringView &key, const StringView &member); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + Output output); + + OptionalDouble zscore(const StringView &key, const StringView &member); + + long long zunionstore(const StringView &destination, const StringView &key, double weight); + + template + long long zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + bool pfadd(const StringView &key, const StringView &element); + + template + bool pfadd(const StringView &key, Input first, Input last); + + template + bool pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + long long pfcount(const StringView &key); + + template + long long pfcount(Input first, Input last); + + template + long long pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + void pfmerge(const StringView &destination, const StringView &key); + + template + void pfmerge(const StringView &destination, Input first, Input last); + + template + void pfmerge(const StringView &destination, std::initializer_list il) { + pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + long long geoadd(const StringView &key, + const std::tuple &member); + + template + long long geoadd(const StringView &key, + Input first, + Input last); + + template + long long geoadd(const StringView &key, + std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + OptionalDouble geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M); + + OptionalString geohash(const StringView &key, const StringView &member); + + template + void geohash(const StringView &key, Input first, Input last, Output output); + + template + void geohash(const StringView &key, std::initializer_list il, Output output) { + geohash(key, il.begin(), il.end(), output); + } + + Optional> geopos(const StringView &key, const StringView &member); + + template + void geopos(const StringView &key, Input first, Input last, Output output); + + template + void geopos(const StringView &key, std::initializer_list il, Output output) { + geopos(key, il.begin(), il.end(), output); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + OptionalLongLong georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // If *output* is an iterator of a container of string, we send *GEORADIUS* command + // without any options and only get the members in the specified geo range. + // If *output* is an iterator of a container of a tuple, the type of the tuple decides + // options we send with the *GEORADIUS* command. If the tuple has an element of type + // double, we send the *WITHDIST* option. If it has an element of type string, we send + // the *WITHHASH* option. If it has an element of type pair, we send + // the *WITHCOORD* option. For example: + // + // The following code only gets the members in range, i.e. without any option. + // + // vector members; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(members)) + // + // The following code sends the command with *WITHDIST* option. + // + // vector> with_dist; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist)) + // + // The following code sends the command with *WITHDIST* and *WITHHASH* options. + // + // vector> with_dist_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_hash)) + // + // The following code sends the command with *WITHDIST*, *WITHCOORD* and *WITHHASH* options. + // + // vector, string>> with_dist_coord_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_coord_hash)) + // + // This also applies to *GEORADIUSBYMEMBER*. + template + void georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + OptionalLongLong georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // See comments on *GEORADIUS*. + template + void georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + // SCRIPTING commands. + + template + Result eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + Result evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + // PUBSUB commands. + + long long publish(const StringView &channel, const StringView &message); + + // Stream commands. + + long long xack(const StringView &key, const StringView &group, const StringView &id); + + template + long long xack(const StringView &key, const StringView &group, Input first, Input last); + + template + long long xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, const StringView &id, Input first, Input last); + + template + std::string xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true); + + template + std::string xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il, + Output output) { + xclaim(key, group, consumer, min_idle_time, il.begin(), il.end(), output); + } + + long long xdel(const StringView &key, const StringView &id); + + template + long long xdel(const StringView &key, Input first, Input last); + + template + long long xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + void xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false); + + void xgroup_setid(const StringView &key, const StringView &group, const StringView &id); + + long long xgroup_destroy(const StringView &key, const StringView &group); + + long long xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer); + + long long xlen(const StringView &key); + + template + auto xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple; + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + Output output) { + xread(key, id, 0, output); + } + + template + auto xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, Input last, Output output) + -> typename std::enable_if::value>::type { + xread(first, last, 0, output); + } + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xread(key, id, timeout, 0, output); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xread(first, last, timeout, 0, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + Output output) { + xreadgroup(group, consumer, key, id, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, 0, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + return xreadgroup(group, consumer, key, id, timeout, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + return xreadgroup(group, consumer, key, id, timeout, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, 0, false, output); + } + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output); + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output); + + long long xtrim(const StringView &key, long long count, bool approx = true); + +private: + class Command { + public: + explicit Command(const StringView &cmd_name) : _cmd_name(cmd_name) {} + + template + void operator()(Connection &connection, Args &&...args) const { + CmdArgs cmd_args; + cmd_args.append(_cmd_name, std::forward(args)...); + connection.send(cmd_args); + } + + private: + StringView _cmd_name; + }; + + template + auto _generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, + ReplyUPtr>::type; + + template + auto _generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type; + + template + ReplyUPtr _command(Cmd cmd, Connection &connection, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, const StringView &key, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, std::true_type, const StringView &key, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, std::false_type, Input &&first, Args &&...args); + + template + ReplyUPtr _command(const StringView &cmd_name, const IndexSequence &, Args &&...args) { + return command(cmd_name, NthValue(std::forward(args)...)...); + } + + template + ReplyUPtr _range_command(Cmd cmd, std::true_type, Input input, Args &&...args); + + template + ReplyUPtr _range_command(Cmd cmd, std::false_type, Input input, Args &&...args); + + void _asking(Connection &connection); + + template + ReplyUPtr _score_command(std::true_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(std::false_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(Cmd cmd, Args &&... args); + + ShardsPool _pool; +}; + +} + +} + +#include "redis_cluster.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.hpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.hpp new file mode 100644 index 000000000..2153d181b --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/redis_cluster.hpp @@ -0,0 +1,1403 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP +#define SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP + +#include +#include "command.h" +#include "reply.h" +#include "utils.h" +#include "errors.h" +#include "shards_pool.h" + +namespace sw { + +namespace redis { + +template +auto RedisCluster::command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type { + return _command(cmd, + std::is_convertible::type, StringView>(), + std::forward(key), + std::forward(args)...); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && !IsIter::type>::value, ReplyUPtr>::type { + auto cmd = Command(cmd_name); + + return _generic_command(cmd, std::forward(key), std::forward(args)...); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if::value + || std::is_arithmetic::type>::value, Result>::type { + auto r = command(cmd_name, std::forward(key), std::forward(args)...); + + assert(r); + + return reply::parse(*r); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && IsIter::type>::value, void>::type { + auto r = _command(cmd_name, + MakeIndexSequence(), + std::forward(key), + std::forward(args)...); + + assert(r); + + reply::to_array(*r, LastValue(std::forward(args)...)); +} + +template +auto RedisCluster::command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (first == last || std::next(first) == last) { + throw Error("command: invalid range"); + } + + const auto &key = *first; + ++first; + + auto cmd = [&key](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + cmd_args.append(key); + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +auto RedisCluster::command(Input first, Input last) + -> typename std::enable_if::value, Result>::type { + auto r = command(first, last); + + assert(r); + + return reply::parse(*r); +} + +template +auto RedisCluster::command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type { + auto r = command(first, last); + + assert(r); + + reply::to_array(*r, output); +} + +// KEY commands. + +template +long long RedisCluster::del(Input first, Input last) { + range_check("DEL", first, last); + + auto reply = command(cmd::del_range, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::exists(Input first, Input last) { + range_check("EXISTS", first, last); + + auto reply = command(cmd::exists_range, first, last); + + return reply::parse(*reply); +} + +inline bool RedisCluster::expire(const StringView &key, const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); +} + +inline bool RedisCluster::expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); +} + +inline bool RedisCluster::pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); +} + +inline bool RedisCluster::pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); +} + +inline void RedisCluster::restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + bool replace) { + return restore(key, val, ttl.count(), replace); +} + +template +long long RedisCluster::touch(Input first, Input last) { + range_check("TOUCH", first, last); + + auto reply = command(cmd::touch_range, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::unlink(Input first, Input last) { + range_check("UNLINK", first, last); + + auto reply = command(cmd::unlink_range, first, last); + + return reply::parse(*reply); +} + +// STRING commands. + +template +long long RedisCluster::bitop(BitOp op, const StringView &destination, Input first, Input last) { + range_check("BITOP", first, last); + + auto reply = _command(cmd::bitop_range, destination, op, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::mget(Input first, Input last, Output output) { + range_check("MGET", first, last); + + auto reply = command(cmd::mget, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::mset(Input first, Input last) { + range_check("MSET", first, last); + + auto reply = command(cmd::mset, first, last); + + reply::parse(*reply); +} + +template +bool RedisCluster::msetnx(Input first, Input last) { + range_check("MSETNX", first, last); + + auto reply = command(cmd::msetnx, first, last); + + return reply::parse(*reply); +} + +inline void RedisCluster::psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); +} + +inline void RedisCluster::setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + setex(key, ttl.count(), val); +} + +// LIST commands. + +template +OptionalStringPair RedisCluster::blpop(Input first, Input last, long long timeout) { + range_check("BLPOP", first, last); + + auto reply = command(cmd::blpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair RedisCluster::blpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return blpop(first, last, timeout.count()); +} + +template +OptionalStringPair RedisCluster::brpop(Input first, Input last, long long timeout) { + range_check("BRPOP", first, last); + + auto reply = command(cmd::brpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair RedisCluster::brpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return brpop(first, last, timeout.count()); +} + +inline OptionalString RedisCluster::brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return brpoplpush(source, destination, timeout.count()); +} + +template +inline long long RedisCluster::lpush(const StringView &key, Input first, Input last) { + range_check("LPUSH", first, last); + + auto reply = command(cmd::lpush_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::lrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = command(cmd::lrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline long long RedisCluster::rpush(const StringView &key, Input first, Input last) { + range_check("RPUSH", first, last); + + auto reply = command(cmd::rpush_range, key, first, last); + + return reply::parse(*reply); +} + +// HASH commands. + +template +inline long long RedisCluster::hdel(const StringView &key, Input first, Input last) { + range_check("HDEL", first, last); + + auto reply = command(cmd::hdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::hgetall(const StringView &key, Output output) { + auto reply = command(cmd::hgetall, key); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hkeys(const StringView &key, Output output) { + auto reply = command(cmd::hkeys, key); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hmget(const StringView &key, Input first, Input last, Output output) { + range_check("HMGET", first, last); + + auto reply = command(cmd::hmget, key, first, last); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hmset(const StringView &key, Input first, Input last) { + range_check("HMSET", first, last); + + auto reply = command(cmd::hmset, key, first, last); + + reply::parse(*reply); +} + +template +long long RedisCluster::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::hscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return hscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return hscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + Output output) { + return hscan(key, cursor, "*", 10, output); +} + +template +auto RedisCluster::hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, + long long>::type { + range_check("HSET", first, last); + + auto reply = command(cmd::hset_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::hvals(const StringView &key, Output output) { + auto reply = command(cmd::hvals, key); + + reply::to_array(*reply, output); +} + +// SET commands. + +template +long long RedisCluster::sadd(const StringView &key, Input first, Input last) { + range_check("SADD", first, last); + + auto reply = command(cmd::sadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::sdiff(Input first, Input last, Output output) { + range_check("SDIFF", first, last); + + auto reply = command(cmd::sdiff, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sdiffstore(const StringView &destination, + Input first, + Input last) { + range_check("SDIFFSTORE", first, last); + + auto reply = command(cmd::sdiffstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::sinter(Input first, Input last, Output output) { + range_check("SINTER", first, last); + + auto reply = command(cmd::sinter, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sinterstore(const StringView &destination, + Input first, + Input last) { + range_check("SINTERSTORE", first, last); + + auto reply = command(cmd::sinterstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::smembers(const StringView &key, Output output) { + auto reply = command(cmd::smembers, key); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::spop(const StringView &key, long long count, Output output) { + auto reply = command(cmd::spop_range, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::srandmember(const StringView &key, long long count, Output output) { + auto reply = command(cmd::srandmember_range, key, count); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::srem(const StringView &key, Input first, Input last) { + range_check("SREM", first, last); + + auto reply = command(cmd::srem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::sscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return sscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return sscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + Output output) { + return sscan(key, cursor, "*", 10, output); +} + +template +void RedisCluster::sunion(Input first, Input last, Output output) { + range_check("SUNION", first, last); + + auto reply = command(cmd::sunion, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sunionstore(const StringView &destination, Input first, Input last) { + range_check("SUNIONSTORE", first, last); + + auto reply = command(cmd::sunionstore_range, destination, first, last); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +inline auto RedisCluster::bzpopmax(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(key, timeout.count()); +} + +template +auto RedisCluster::bzpopmax(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto RedisCluster::bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(first, last, timeout.count()); +} + +inline auto RedisCluster::bzpopmin(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(key, timeout.count()); +} + +template +auto RedisCluster::bzpopmin(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto RedisCluster::bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(first, last, timeout.count()); +} + +template +long long RedisCluster::zadd(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + range_check("ZADD", first, last); + + auto reply = command(cmd::zadd_range, key, first, last, type, changed); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zcount, key, interval); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZINTERSTORE", first, last); + + auto reply = command(cmd::zinterstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zlexcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zlexcount, key, interval); + + return reply::parse(*reply); +} + +template +void RedisCluster::zpopmax(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmax, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zpopmin(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmin, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrangebylex(const StringView &key, const Interval &interval, Output output) { + zrangebylex(key, interval, {}, output); +} + +template +void RedisCluster::zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrangebyscore(const StringView &key, + const Interval &interval, + Output output) { + zrangebyscore(key, interval, {}, output); +} + +template +void RedisCluster::zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrangebyscore, + key, + interval, + opts); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::zrem(const StringView &key, Input first, Input last) { + range_check("ZREM", first, last); + + auto reply = command(cmd::zrem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zremrangebylex(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebylex, key, interval); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zremrangebyscore(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebyscore, key, interval); + + return reply::parse(*reply); +} + +template +void RedisCluster::zrevrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrevrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::zrevrangebylex(const StringView &key, + const Interval &interval, + Output output) { + zrevrangebylex(key, interval, {}, output); +} + +template +void RedisCluster::zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrevrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrevrangebyscore(const StringView &key, const Interval &interval, Output output) { + zrevrangebyscore(key, interval, {}, output); +} + +template +void RedisCluster::zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrevrangebyscore, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::zscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return zscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return zscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + Output output) { + return zscan(key, cursor, "*", 10, output); +} + +template +long long RedisCluster::zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZUNIONSTORE", first, last); + + auto reply = command(cmd::zunionstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +template +bool RedisCluster::pfadd(const StringView &key, Input first, Input last) { + range_check("PFADD", first, last); + + auto reply = command(cmd::pfadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::pfcount(Input first, Input last) { + range_check("PFCOUNT", first, last); + + auto reply = command(cmd::pfcount_range, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::pfmerge(const StringView &destination, + Input first, + Input last) { + range_check("PFMERGE", first, last); + + auto reply = command(cmd::pfmerge_range, destination, first, last); + + reply::parse(*reply); +} + +// GEO commands. + +template +inline long long RedisCluster::geoadd(const StringView &key, + Input first, + Input last) { + range_check("GEOADD", first, last); + + auto reply = command(cmd::geoadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::geohash(const StringView &key, Input first, Input last, Output output) { + range_check("GEOHASH", first, last); + + auto reply = command(cmd::geohash_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::geopos(const StringView &key, Input first, Input last, Output output) { + range_check("GEOPOS", first, last); + + auto reply = command(cmd::geopos_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +// SCRIPTING commands. + +template +Result RedisCluster::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::eval, *keys_first, script, keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result RedisCluster::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return eval(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void RedisCluster::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::eval, + *keys_first, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + eval(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +template +Result RedisCluster::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::evalsha, *keys_first, script, + keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result RedisCluster::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return evalsha(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void RedisCluster::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::evalsha, + *keys_first, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + evalsha(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +// Stream commands. + +template +long long RedisCluster::xack(const StringView &key, + const StringView &group, + Input first, + Input last) { + auto reply = command(cmd::xack_range, key, group, first, last); + + return reply::parse(*reply); +} + +template +std::string RedisCluster::xadd(const StringView &key, + const StringView &id, + Input first, + Input last) { + auto reply = command(cmd::xadd_range, key, id, first, last); + + return reply::parse(*reply); +} + +template +std::string RedisCluster::xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + auto reply = command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + + return reply::parse(*reply); +} + +template +void RedisCluster::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output) { + auto reply = command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output) { + auto reply = command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::xdel(const StringView &key, Input first, Input last) { + auto reply = command(cmd::xdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +auto RedisCluster::xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple { + auto reply = command(cmd::xpending, key, group); + + return reply::parse_xpending_reply(*reply, output); +} + +template +void RedisCluster::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xpending_detail, key, group, start, end, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output) { + auto reply = command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output) { + auto reply = command(cmd::xrange, key, start, end); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xrange_count, key, start, end, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xread(const StringView &key, + const StringView &id, + long long count, + Output output) { + auto reply = command(cmd::xread, key, id, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_range, first, last, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + auto reply = command(cmd::xread_block, key, id, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_block_range, first, last, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output) { + auto reply = _command(cmd::xreadgroup, key, group, consumer, key, id, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = _command(cmd::xreadgroup_range, + first->first, + group, + consumer, + first, + last, + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) { + auto reply = _command(cmd::xreadgroup_block, + key, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = _command(cmd::xreadgroup_block_range, + first->first, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output) { + auto reply = command(cmd::xrevrange, key, end, start); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output) { + auto reply = command(cmd::xrevrange_count, key, end, start, count); + + reply::to_array(*reply, output); +} + +template +auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, + ReplyUPtr>::type { + return command(cmd, std::forward(key), std::forward(args)...); +} + +template +auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type { + auto k = std::to_string(std::forward(key)); + return command(cmd, k, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, std::true_type, const StringView &key, Args &&...args) { + return _command(cmd, key, key, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, std::false_type, Input &&first, Args &&...args) { + return _range_command(cmd, + std::is_convertible< + typename std::decay< + decltype(*std::declval())>::type, StringView>(), + std::forward(first), + std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::true_type, Input input, Args &&...args) { + return _command(cmd, *input, input, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::false_type, Input input, Args &&...args) { + return _command(cmd, std::get<0>(*input), input, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, Connection &connection, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + + return connection.recv(); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, const StringView &key, Args &&...args) { + for (auto idx = 0; idx < 2; ++idx) { + try { + auto pool = _pool.fetch(key); + assert(pool); + SafeConnection safe_connection(*pool); + + return _command(cmd, safe_connection.connection(), std::forward(args)...); + } catch (const IoError &err) { + // When master is down, one of its replicas will be promoted to be the new master. + // If we try to send command to the old master, we'll get an *IoError*. + // In this case, we need to update the slots mapping. + _pool.update(); + } catch (const ClosedError &err) { + // Node might be removed. + // 1. Get up-to-date slot mapping to check if the node still exists. + _pool.update(); + + // TODO: + // 2. If it's NOT exist, update slot mapping, and retry. + // 3. If it's still exist, that means the node is down, NOT removed, throw exception. + } catch (const MovedError &err) { + // Slot mapping has been changed, update it and try again. + _pool.update(); + } catch (const AskError &err) { + auto pool = _pool.fetch(err.node()); + assert(pool); + SafeConnection safe_connection(*pool); + auto &connection = safe_connection.connection(); + + // 1. send ASKING command. + _asking(connection); + + // 2. resend last command. + try { + return _command(cmd, connection, std::forward(args)...); + } catch (const MovedError &err) { + throw Error("Slot migrating... ASKING node hasn't been set to IMPORTING state"); + } + } // For other exceptions, just throw it. + } + + // Possible failures: + // 1. Source node has already run 'CLUSTER SETSLOT xxx NODE xxx', + // while the destination node has NOT run it. + // In this case, client will be redirected by both nodes with MovedError. + // 2. Other failures... + throw Error("Failed to send command with key: " + std::string(key.data(), key.size())); +} + +template +inline ReplyUPtr RedisCluster::_score_command(std::true_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., true); +} + +template +inline ReplyUPtr RedisCluster::_score_command(std::false_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., false); +} + +template +inline ReplyUPtr RedisCluster::_score_command(Cmd cmd, Args &&... args) { + return _score_command(typename IsKvPairIter::type(), + cmd, + std::forward(args)...); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/reply.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/reply.cpp new file mode 100644 index 000000000..01f1c8eec --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/reply.cpp @@ -0,0 +1,158 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "reply.h" +#include +#include + +namespace sw { + +namespace redis { + +namespace reply { + +std::string to_status(redisReply &reply) { + if (!reply::is_status(reply)) { + throw ProtoError("Expect STATUS reply"); + } + + if (reply.str == nullptr) { + throw ProtoError("A null status reply"); + } + + // Old version hiredis' *redisReply::len* is of type int. + // So we CANNOT have something like: *return {reply.str, reply.len}*. + return std::string(reply.str, reply.len); +} + +std::string parse(ParseTag, redisReply &reply) { + if (!reply::is_string(reply) && !reply::is_status(reply)) { + throw ProtoError("Expect STRING reply"); + } + + if (reply.str == nullptr) { + throw ProtoError("A null string reply"); + } + + // Old version hiredis' *redisReply::len* is of type int. + // So we CANNOT have something like: *return {reply.str, reply.len}*. + return std::string(reply.str, reply.len); +} + +long long parse(ParseTag, redisReply &reply) { + if (!reply::is_integer(reply)) { + throw ProtoError("Expect INTEGER reply"); + } + + return reply.integer; +} + +double parse(ParseTag, redisReply &reply) { + try { + return std::stod(parse(reply)); + } catch (const std::invalid_argument &) { + throw ProtoError("not a double reply"); + } catch (const std::out_of_range &) { + throw ProtoError("double reply out of range"); + } +} + +bool parse(ParseTag, redisReply &reply) { + auto ret = parse(reply); + + if (ret == 1) { + return true; + } else if (ret == 0) { + return false; + } else { + throw ProtoError("Invalid bool reply: " + std::to_string(ret)); + } +} + +void parse(ParseTag, redisReply &reply) { + if (!reply::is_status(reply)) { + throw ProtoError("Expect STATUS reply"); + } + + if (reply.str == nullptr) { + throw ProtoError("A null status reply"); + } + + static const std::string OK = "OK"; + + // Old version hiredis' *redisReply::len* is of type int. + // So we have to cast it to an unsigned int. + if (static_cast(reply.len) != OK.size() + || OK.compare(0, OK.size(), reply.str, reply.len) != 0) { + throw ProtoError("NOT ok status reply: " + reply::to_status(reply)); + } +} + +void rewrite_set_reply(redisReply &reply) { + if (is_nil(reply)) { + // Failed to set, and make it a FALSE reply. + reply.type = REDIS_REPLY_INTEGER; + reply.integer = 0; + + return; + } + + // Check if it's a "OK" status reply. + reply::parse(reply); + + assert(is_status(reply) && reply.str != nullptr); + + free(reply.str); + + // Make it a TRUE reply. + reply.type = REDIS_REPLY_INTEGER; + reply.integer = 1; +} + +void rewrite_empty_array_reply(redisReply &reply) { + if (is_array(reply) && reply.elements == 0) { + // Make it a nil reply. + reply.type = REDIS_REPLY_NIL; + } +} + +namespace detail { + +bool is_flat_array(redisReply &reply) { + assert(reply::is_array(reply)); + + // Empty array reply. + if (reply.element == nullptr || reply.elements == 0) { + return false; + } + + auto *sub_reply = reply.element[0]; + + // Null element. + if (sub_reply == nullptr) { + return false; + } + + return !reply::is_array(*sub_reply); +} + +} + +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/reply.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/reply.h new file mode 100644 index 000000000..d89174329 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/reply.h @@ -0,0 +1,435 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REPLY_H +#define SEWENEW_REDISPLUSPLUS_REPLY_H + +#include +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +struct ReplyDeleter { + void operator()(redisReply *reply) const { + if (reply != nullptr) { + freeReplyObject(reply); + } + } +}; + +using ReplyUPtr = std::unique_ptr; + +namespace reply { + +template +struct ParseTag {}; + +template +inline T parse(redisReply &reply) { + return parse(ParseTag(), reply); +} + +template +T parse_leniently(redisReply &reply); + +void parse(ParseTag, redisReply &reply); + +std::string parse(ParseTag, redisReply &reply); + +long long parse(ParseTag, redisReply &reply); + +double parse(ParseTag, redisReply &reply); + +bool parse(ParseTag, redisReply &reply); + +template +Optional parse(ParseTag>, redisReply &reply); + +template +std::pair parse(ParseTag>, redisReply &reply); + +template +std::tuple parse(ParseTag>, redisReply &reply); + +#ifdef REDIS_PLUS_PLUS_HAS_VARIANT + +inline Monostate parse(ParseTag, redisReply &) { + // Just ignore the reply + return {}; +} + +template +Variant parse(ParseTag>, redisReply &reply); + +#endif + +template ::value, int>::type = 0> +T parse(ParseTag, redisReply &reply); + +template ::value, int>::type = 0> +T parse(ParseTag, redisReply &reply); + +template +long long parse_scan_reply(redisReply &reply, Output output); + +inline bool is_error(redisReply &reply) { + return reply.type == REDIS_REPLY_ERROR; +} + +inline bool is_nil(redisReply &reply) { + return reply.type == REDIS_REPLY_NIL; +} + +inline bool is_string(redisReply &reply) { + return reply.type == REDIS_REPLY_STRING; +} + +inline bool is_status(redisReply &reply) { + return reply.type == REDIS_REPLY_STATUS; +} + +inline bool is_integer(redisReply &reply) { + return reply.type == REDIS_REPLY_INTEGER; +} + +inline bool is_array(redisReply &reply) { + return reply.type == REDIS_REPLY_ARRAY; +} + +std::string to_status(redisReply &reply); + +template +void to_array(redisReply &reply, Output output); + +// Rewrite set reply to bool type +void rewrite_set_reply(redisReply &reply); + +// Some command might return an empty array reply as a nil reply, +// e.g. georadius, zpopmin, zpopmax. In this case, we rewrite the +// reply to a nil reply. +void rewrite_empty_array_reply(redisReply &reply); + +template +auto parse_xpending_reply(redisReply &reply, Output output) + -> std::tuple; + +} + +// Inline implementations. + +namespace reply { + +namespace detail { + +template +void to_array(redisReply &reply, Output output) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.element == nullptr) { + // Empty array. + return; + } + + for (std::size_t idx = 0; idx != reply.elements; ++idx) { + auto *sub_reply = reply.element[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null array element reply"); + } + + *output = parse::type>(*sub_reply); + + ++output; + } +} + +bool is_flat_array(redisReply &reply); + +template +void to_flat_array(redisReply &reply, Output output) { + if (reply.element == nullptr) { + // Empty array. + return; + } + + if (reply.elements % 2 != 0) { + throw ProtoError("Not string pair array reply"); + } + + for (std::size_t idx = 0; idx != reply.elements; idx += 2) { + auto *key_reply = reply.element[idx]; + auto *val_reply = reply.element[idx + 1]; + if (key_reply == nullptr || val_reply == nullptr) { + throw ProtoError("Null string array reply"); + } + + using Pair = typename IterType::type; + using FirstType = typename std::decay::type; + using SecondType = typename std::decay::type; + *output = std::make_pair(parse(*key_reply), + parse(*val_reply)); + + ++output; + } +} + +template +void to_array(std::true_type, redisReply &reply, Output output) { + if (is_flat_array(reply)) { + to_flat_array(reply, output); + } else { + to_array(reply, output); + } +} + +template +void to_array(std::false_type, redisReply &reply, Output output) { + to_array(reply, output); +} + +template +std::tuple parse_tuple(redisReply **reply, std::size_t idx) { + assert(reply != nullptr); + + auto *sub_reply = reply[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null reply"); + } + + return std::make_tuple(parse(*sub_reply)); +} + +template +auto parse_tuple(redisReply **reply, std::size_t idx) -> + typename std::enable_if>::type { + assert(reply != nullptr); + + return std::tuple_cat(parse_tuple(reply, idx), + parse_tuple(reply, idx + 1)); +} + +#ifdef REDIS_PLUS_PLUS_HAS_VARIANT + +template +Variant parse_variant(redisReply &reply) { + return parse(reply); +} + +template +auto parse_variant(redisReply &reply) -> + typename std::enable_if>::type { + auto return_var = [](auto &&arg) { + return Variant(std::move(arg)); + }; + + try { + return std::visit(return_var, parse_variant(reply)); + } catch (const ProtoError &) { + return std::visit(return_var, parse_variant(reply)); + } +} + +#endif + +} + +template +T parse_leniently(redisReply &reply) { + if (is_array(reply) && reply.elements == 1) { + if (reply.element == nullptr) { + throw ProtoError("null array reply"); + } + + auto *ele = reply.element[0]; + if (ele != nullptr) { + return parse(*ele); + } // else fall through + } + + return parse(reply); +} + +template +Optional parse(ParseTag>, redisReply &reply) { + if (reply::is_nil(reply)) { + // Because of a GCC bug, we cannot return {} for -std=c++17 + // Refer to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86465 +#if defined REDIS_PLUS_PLUS_HAS_OPTIONAL + return std::nullopt; +#else + return {}; +#endif + } + + return Optional(parse(reply)); +} + +template +std::pair parse(ParseTag>, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.elements != 2) { + throw ProtoError("NOT key-value PAIR reply"); + } + + if (reply.element == nullptr) { + throw ProtoError("Null PAIR reply"); + } + + auto *first = reply.element[0]; + auto *second = reply.element[1]; + if (first == nullptr || second == nullptr) { + throw ProtoError("Null pair reply"); + } + + return std::make_pair(parse::type>(*first), + parse::type>(*second)); +} + +template +std::tuple parse(ParseTag>, redisReply &reply) { + constexpr auto size = sizeof...(Args); + + static_assert(size > 0, "DO NOT support parsing tuple with 0 element"); + + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.elements != size) { + throw ProtoError("Expect tuple reply with " + std::to_string(size) + "elements"); + } + + if (reply.element == nullptr) { + throw ProtoError("Null TUPLE reply"); + } + + return detail::parse_tuple(reply.element, 0); +} + +#ifdef REDIS_PLUS_PLUS_HAS_VARIANT + +template +Variant parse(ParseTag>, redisReply &reply) { + return detail::parse_variant(reply); +} + +#endif + +template ::value, int>::type> +T parse(ParseTag, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + T container; + + to_array(reply, std::back_inserter(container)); + + return container; +} + +template ::value, int>::type> +T parse(ParseTag, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + T container; + + to_array(reply, std::inserter(container, container.end())); + + return container; +} + +template +long long parse_scan_reply(redisReply &reply, Output output) { + if (reply.elements != 2 || reply.element == nullptr) { + throw ProtoError("Invalid scan reply"); + } + + auto *cursor_reply = reply.element[0]; + auto *data_reply = reply.element[1]; + if (cursor_reply == nullptr || data_reply == nullptr) { + throw ProtoError("Invalid cursor reply or data reply"); + } + + auto cursor_str = reply::parse(*cursor_reply); + long long new_cursor = 0; + try { + new_cursor = std::stoll(cursor_str); + } catch (const std::exception &e) { + throw ProtoError("Invalid cursor reply: " + cursor_str); + } + + reply::to_array(*data_reply, output); + + return new_cursor; +} + +template +void to_array(redisReply &reply, Output output) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + detail::to_array(typename IsKvPairIter::type(), reply, output); +} + +template +auto parse_xpending_reply(redisReply &reply, Output output) + -> std::tuple { + if (!is_array(reply) || reply.elements != 4) { + throw ProtoError("expect array reply with 4 elements"); + } + + for (std::size_t idx = 0; idx != reply.elements; ++idx) { + if (reply.element[idx] == nullptr) { + throw ProtoError("null array reply"); + } + } + + auto num = parse(*(reply.element[0])); + auto start = parse(*(reply.element[1])); + auto end = parse(*(reply.element[2])); + + auto &entry_reply = *(reply.element[3]); + if (!is_nil(entry_reply)) { + to_array(entry_reply, output); + } + + return std::make_tuple(num, std::move(start), std::move(end)); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REPLY_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/sentinel.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/sentinel.cpp new file mode 100644 index 000000000..6cce61461 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/sentinel.cpp @@ -0,0 +1,362 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "sentinel.h" +#include +#include +#include +#include +#include "redis.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class Sentinel::Iterator { +public: + Iterator(std::list &healthy_sentinels, + std::list &broken_sentinels) : + _healthy_sentinels(healthy_sentinels), + _broken_sentinels(broken_sentinels) { + reset(); + } + + Connection& next(); + + void reset(); + +private: + std::list &_healthy_sentinels; + + std::size_t _healthy_size = 0; + + std::list &_broken_sentinels; + + std::size_t _broken_size = 0; +}; + +Connection& Sentinel::Iterator::next() { + while (_healthy_size > 0) { + assert(_healthy_sentinels.size() >= _healthy_size); + + --_healthy_size; + + auto &connection = _healthy_sentinels.front(); + if (connection.broken()) { + _broken_sentinels.push_front(connection.options()); + ++_broken_size; + + _healthy_sentinels.pop_front(); + } else { + _healthy_sentinels.splice(_healthy_sentinels.end(), + _healthy_sentinels, + _healthy_sentinels.begin()); + + return _healthy_sentinels.back(); + } + } + + while (_broken_size > 0) { + assert(_broken_sentinels.size() >= _broken_size); + + --_broken_size; + + try { + const auto &opt = _broken_sentinels.front(); + Connection connection(opt); + _healthy_sentinels.push_back(std::move(connection)); + + _broken_sentinels.pop_front(); + + return _healthy_sentinels.back(); + } catch (const Error &e) { + // Failed to connect to sentinel. + _broken_sentinels.splice(_broken_sentinels.end(), + _broken_sentinels, + _broken_sentinels.begin()); + } + } + + throw StopIterError(); +} + +void Sentinel::Iterator::reset() { + _healthy_size = _healthy_sentinels.size(); + _broken_size = _broken_sentinels.size(); +} + +Sentinel::Sentinel(const SentinelOptions &sentinel_opts) : + _broken_sentinels(_parse_options(sentinel_opts)), + _sentinel_opts(sentinel_opts) { + if (_sentinel_opts.connect_timeout == std::chrono::milliseconds(0) + || _sentinel_opts.socket_timeout == std::chrono::milliseconds(0)) { + throw Error("With sentinel, connection timeout and socket timeout cannot be 0"); + } +} + +Connection Sentinel::master(const std::string &master_name, const ConnectionOptions &opts) { + std::lock_guard lock(_mutex); + + Iterator iter(_healthy_sentinels, _broken_sentinels); + std::size_t retries = 0; + while (true) { + try { + auto &sentinel = iter.next(); + + auto master = _get_master_addr_by_name(sentinel, master_name); + if (!master) { + // Try the next sentinel. + continue; + } + + auto connection = _connect_redis(*master, opts); + if (_get_role(connection) != Role::MASTER) { + // Retry the whole process at most SentinelOptions::max_retry times. + ++retries; + if (retries > _sentinel_opts.max_retry) { + throw Error("Failed to get master from sentinel"); + } + + std::this_thread::sleep_for(_sentinel_opts.retry_interval); + + // Restart the iteration. + iter.reset(); + continue; + } + + return connection; + } catch (const StopIterError &e) { + throw; + } catch (const Error &e) { + continue; + } + } +} + +Connection Sentinel::slave(const std::string &master_name, const ConnectionOptions &opts) { + std::lock_guard lock(_mutex); + + Iterator iter(_healthy_sentinels, _broken_sentinels); + std::size_t retries = 0; + while (true) { + try { + auto &sentinel = iter.next(); + + auto slaves = _get_slave_addr_by_name(sentinel, master_name); + if (slaves.empty()) { + // Try the next sentinel. + continue; + } + + // Normally slaves list is NOT very large, so there won't be a performance problem. + auto slave_iter = std::find(slaves.begin(), + slaves.end(), + Node{opts.host, opts.port}); + if (slave_iter != slaves.end() && slave_iter != slaves.begin()) { + // The given node is still a valid slave. Try it first. + std::swap(*(slaves.begin()), *slave_iter); + } + + for (const auto &slave : slaves) { + try { + auto connection = _connect_redis(slave, opts); + if (_get_role(connection) != Role::SLAVE) { + // Retry the whole process at most SentinelOptions::max_retry times. + ++retries; + if (retries > _sentinel_opts.max_retry) { + throw Error("Failed to get slave from sentinel"); + } + + std::this_thread::sleep_for(_sentinel_opts.retry_interval); + + // Restart the iteration. + iter.reset(); + break; + } + + return connection; + } catch (const Error &e) { + // Try the next slave. + continue; + } + } + } catch (const StopIterError &e) { + throw; + } catch (const Error &e) { + continue; + } + } +} + +Optional Sentinel::_get_master_addr_by_name(Connection &connection, const StringView &name) { + connection.send("SENTINEL GET-MASTER-ADDR-BY-NAME %b", name.data(), name.size()); + + auto reply = connection.recv(); + + assert(reply); + + auto master = reply::parse>>(*reply); + if (!master) { + return {}; + } + + int port = 0; + try { + port = std::stoi(master->second); + } catch (const std::exception &) { + throw ProtoError("Master port is invalid: " + master->second); + } + + return Optional{Node{master->first, port}}; +} + +std::vector Sentinel::_get_slave_addr_by_name(Connection &connection, + const StringView &name) { + try { + connection.send("SENTINEL SLAVES %b", name.data(), name.size()); + + auto reply = connection.recv(); + + assert(reply); + + auto slaves = _parse_slave_info(*reply); + + // Make slave list random. + std::mt19937 gen(std::random_device{}()); + std::shuffle(slaves.begin(), slaves.end(), gen); + + return slaves; + } catch (const ReplyError &e) { + // Unknown master name. + return {}; + } +} + +std::vector Sentinel::_parse_slave_info(redisReply &reply) const { + using SlaveInfo = std::unordered_map; + + auto slaves = reply::parse>(reply); + + std::vector nodes; + for (const auto &slave : slaves) { + auto flags_iter = slave.find("flags"); + auto ip_iter = slave.find("ip"); + auto port_iter = slave.find("port"); + if (flags_iter == slave.end() || ip_iter == slave.end() || port_iter == slave.end()) { + throw ProtoError("Invalid slave info"); + } + + // This slave is down, e.g. 's_down,slave,disconnected' + if (flags_iter->second != "slave") { + continue; + } + + int port = 0; + try { + port = std::stoi(port_iter->second); + } catch (const std::exception &) { + throw ProtoError("Slave port is invalid: " + port_iter->second); + } + + nodes.push_back(Node{ip_iter->second, port}); + } + + return nodes; +} + +Connection Sentinel::_connect_redis(const Node &node, ConnectionOptions opts) { + opts.host = node.host; + opts.port = node.port; + + return Connection(opts); +} + +Role Sentinel::_get_role(Connection &connection) { + connection.send("INFO REPLICATION"); + auto reply = connection.recv(); + + assert(reply); + auto info = reply::parse(*reply); + + auto start = info.find("role:"); + if (start == std::string::npos) { + throw ProtoError("Invalid INFO REPLICATION reply"); + } + start += 5; + auto stop = info.find("\r\n", start); + if (stop == std::string::npos) { + throw ProtoError("Invalid INFO REPLICATION reply"); + } + + auto role = info.substr(start, stop - start); + if (role == "master") { + return Role::MASTER; + } else if (role == "slave") { + return Role::SLAVE; + } else { + throw Error("Invalid role: " + role); + } +} + +std::list Sentinel::_parse_options(const SentinelOptions &opts) const { + std::list options; + for (const auto &node : opts.nodes) { + ConnectionOptions opt; + opt.host = node.first; + opt.port = node.second; + opt.password = opts.password; + opt.keep_alive = opts.keep_alive; + opt.connect_timeout = opts.connect_timeout; + opt.socket_timeout = opts.socket_timeout; + opt.tls = opts.tls; + + options.push_back(opt); + } + + return options; +} + +SimpleSentinel::SimpleSentinel(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role) : + _sentinel(sentinel), + _master_name(master_name), + _role(role) { + if (!_sentinel) { + throw Error("Sentinel cannot be null"); + } + + if (_role != Role::MASTER && _role != Role::SLAVE) { + throw Error("Role must be Role::MASTER or Role::SLAVE"); + } +} + +Connection SimpleSentinel::create(const ConnectionOptions &opts) { + assert(_sentinel); + + if (_role == Role::MASTER) { + return _sentinel->master(_master_name, opts); + } + + assert(_role == Role::SLAVE); + + return _sentinel->slave(_master_name, opts); +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/sentinel.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/sentinel.h new file mode 100644 index 000000000..6f998791e --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/sentinel.h @@ -0,0 +1,141 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SENTINEL_H +#define SEWENEW_REDISPLUSPLUS_SENTINEL_H + +#include +#include +#include +#include +#include +#include "connection.h" +#include "shards.h" +#include "reply.h" +#include "tls.h" + +namespace sw { + +namespace redis { + +struct SentinelOptions { + std::vector> nodes; + + std::string password; + + bool keep_alive = true; + + std::chrono::milliseconds connect_timeout{100}; + + std::chrono::milliseconds socket_timeout{100}; + + std::chrono::milliseconds retry_interval{100}; + + std::size_t max_retry = 2; + + tls::TlsOptions tls; +}; + +class Sentinel { +public: + explicit Sentinel(const SentinelOptions &sentinel_opts); + + Sentinel(const Sentinel &) = delete; + Sentinel& operator=(const Sentinel &) = delete; + + Sentinel(Sentinel &&) = delete; + Sentinel& operator=(Sentinel &&) = delete; + + ~Sentinel() = default; + +private: + Connection master(const std::string &master_name, const ConnectionOptions &opts); + + Connection slave(const std::string &master_name, const ConnectionOptions &opts); + + class Iterator; + + friend class SimpleSentinel; + + std::list _parse_options(const SentinelOptions &opts) const; + + Optional _get_master_addr_by_name(Connection &connection, const StringView &name); + + std::vector _get_slave_addr_by_name(Connection &connection, const StringView &name); + + Connection _connect_redis(const Node &node, ConnectionOptions opts); + + Role _get_role(Connection &connection); + + std::vector _parse_slave_info(redisReply &reply) const; + + std::list _healthy_sentinels; + + std::list _broken_sentinels; + + SentinelOptions _sentinel_opts; + + std::mutex _mutex; +}; + +class SimpleSentinel { +public: + SimpleSentinel(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role); + + SimpleSentinel() = default; + + SimpleSentinel(const SimpleSentinel &) = default; + SimpleSentinel& operator=(const SimpleSentinel &) = default; + + SimpleSentinel(SimpleSentinel &&) = default; + SimpleSentinel& operator=(SimpleSentinel &&) = default; + + ~SimpleSentinel() = default; + + explicit operator bool() const { + return bool(_sentinel); + } + + Connection create(const ConnectionOptions &opts); + +private: + std::shared_ptr _sentinel; + + std::string _master_name; + + Role _role = Role::MASTER; +}; + +class StopIterError : public Error { +public: + StopIterError() : Error("StopIterError") {} + + StopIterError(const StopIterError &) = default; + StopIterError& operator=(const StopIterError &) = default; + + StopIterError(StopIterError &&) = default; + StopIterError& operator=(StopIterError &&) = default; + + virtual ~StopIterError() override = default; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SENTINEL_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards.cpp new file mode 100644 index 000000000..c5baf5ad8 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards.cpp @@ -0,0 +1,52 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "shards.h" + +namespace sw { + +namespace redis { + +RedirectionError::RedirectionError(const std::string &msg): ReplyError(msg) { + std::tie(_slot, _node) = _parse_error(msg); +} + +std::pair RedirectionError::_parse_error(const std::string &msg) const { + // "slot ip:port" + auto space_pos = msg.find(" "); + auto colon_pos = msg.find(":"); + if (space_pos == std::string::npos + || colon_pos == std::string::npos + || colon_pos < space_pos) { + throw ProtoError("Invalid ASK error message: " + msg); + } + + try { + // We need to do a cast for x86 build (32 bit) on Windows. + // See https://github.com/sewenew/redis-plus-plus/issues/115 for detail. + auto slot = static_cast(std::stoull(msg.substr(0, space_pos))); + auto host = msg.substr(space_pos + 1, colon_pos - space_pos - 1); + auto port = std::stoi(msg.substr(colon_pos + 1)); + + return {slot, {host, port}}; + } catch (const std::exception &e) { + throw ProtoError("Invalid ASK error message: " + msg); + } +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards.h new file mode 100644 index 000000000..0d4cd97b3 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards.h @@ -0,0 +1,115 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_H +#define SEWENEW_REDISPLUSPLUS_SHARDS_H + +#include +#include +#include "errors.h" + +namespace sw { + +namespace redis { + +using Slot = std::size_t; + +struct SlotRange { + Slot min; + Slot max; +}; + +inline bool operator<(const SlotRange &lhs, const SlotRange &rhs) { + return lhs.max < rhs.max; +} + +struct Node { + std::string host; + int port; +}; + +inline bool operator==(const Node &lhs, const Node &rhs) { + return lhs.host == rhs.host && lhs.port == rhs.port; +} + +struct NodeHash { + std::size_t operator()(const Node &node) const noexcept { + auto host_hash = std::hash{}(node.host); + auto port_hash = std::hash{}(node.port); + return host_hash ^ (port_hash << 1); + } +}; + +using Shards = std::map; + +class RedirectionError : public ReplyError { +public: + RedirectionError(const std::string &msg); + + RedirectionError(const RedirectionError &) = default; + RedirectionError& operator=(const RedirectionError &) = default; + + RedirectionError(RedirectionError &&) = default; + RedirectionError& operator=(RedirectionError &&) = default; + + virtual ~RedirectionError() override = default; + + Slot slot() const { + return _slot; + } + + const Node& node() const { + return _node; + } + +private: + std::pair _parse_error(const std::string &msg) const; + + Slot _slot = 0; + Node _node; +}; + +class MovedError : public RedirectionError { +public: + explicit MovedError(const std::string &msg) : RedirectionError(msg) {} + + MovedError(const MovedError &) = default; + MovedError& operator=(const MovedError &) = default; + + MovedError(MovedError &&) = default; + MovedError& operator=(MovedError &&) = default; + + virtual ~MovedError() override = default; +}; + +class AskError : public RedirectionError { +public: + explicit AskError(const std::string &msg) : RedirectionError(msg) {} + + AskError(const AskError &) = default; + AskError& operator=(const AskError &) = default; + + AskError(AskError &&) = default; + AskError& operator=(AskError &&) = default; + + virtual ~AskError() override = default; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards_pool.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards_pool.cpp new file mode 100644 index 000000000..e12231639 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards_pool.cpp @@ -0,0 +1,369 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "shards_pool.h" +#include +#include "errors.h" + +namespace sw { + +namespace redis { + +const std::size_t ShardsPool::SHARDS; + +ShardsPool::ShardsPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts, + Role role) : + _pool_opts(pool_opts), + _connection_opts(connection_opts), + _role(role) { + if (_connection_opts.type != ConnectionType::TCP) { + throw Error("Only support TCP connection for Redis Cluster"); + } + + Connection connection(_connection_opts); + + _shards = _cluster_slots(connection); + + _init_pool(_shards); +} + +ShardsPool::ShardsPool(ShardsPool &&that) { + std::lock_guard lock(that._mutex); + + _move(std::move(that)); +} + +ShardsPool& ShardsPool::operator=(ShardsPool &&that) { + if (this != &that) { + std::lock(_mutex, that._mutex); + std::lock_guard lock_this(_mutex, std::adopt_lock); + std::lock_guard lock_that(that._mutex, std::adopt_lock); + + _move(std::move(that)); + } + + return *this; +} + +ConnectionPoolSPtr ShardsPool::fetch(const StringView &key) { + auto slot = _slot(key); + + return _fetch(slot); +} + +ConnectionPoolSPtr ShardsPool::fetch() { + auto slot = _slot(); + + return _fetch(slot); +} + +ConnectionPoolSPtr ShardsPool::fetch(const Node &node) { + std::lock_guard lock(_mutex); + + auto iter = _pools.find(node); + if (iter == _pools.end()) { + // Node doesn't exist, and it should be a newly created node. + // So add a new connection pool. + iter = _add_node(node); + } + + assert(iter != _pools.end()); + + return iter->second; +} + +void ShardsPool::update() { + // My might send command to a removed node. + // Try at most 3 times from the current shard masters and finally with the user given connection options. + for (auto idx = 0; idx < 4; ++idx) { + try { + Shards shards; + if (idx < 3) { + // Randomly pick a connection. + auto pool = fetch(); + assert(pool); + SafeConnection safe_connection(*pool); + shards = _cluster_slots(safe_connection.connection()); + } + else { + Connection connection(_connection_opts); + shards = _cluster_slots(connection); + } + + + std::unordered_set nodes; + for (const auto &shard : shards) { + nodes.insert(shard.second); + } + + std::lock_guard lock(_mutex); + + // TODO: If shards is unchanged, no need to update, and return immediately. + + _shards = std::move(shards); + + // Remove non-existent nodes. + for (auto iter = _pools.begin(); iter != _pools.end(); ) { + if (nodes.find(iter->first) == nodes.end()) { + // Node has been removed. + _pools.erase(iter++); + } else { + ++iter; + } + } + + // Add connection pool for new nodes. + // In fact, connections will be created lazily. + for (const auto &node : nodes) { + if (_pools.find(node) == _pools.end()) { + _add_node(node); + } + } + + // Update successfully. + return; + } catch (const Error &) { + // continue; + } + } + + throw Error("Failed to update shards info"); +} + +ConnectionOptions ShardsPool::connection_options(const StringView &key) { + auto slot = _slot(key); + + return _connection_options(slot); +} + +ConnectionOptions ShardsPool::connection_options() { + auto slot = _slot(); + + return _connection_options(slot); +} + +Shards ShardsPool::shards() { + std::lock_guard lock(_mutex); + + return _shards; +} + +void ShardsPool::_move(ShardsPool &&that) { + _pool_opts = that._pool_opts; + _connection_opts = that._connection_opts; + _shards = std::move(that._shards); + _pools = std::move(that._pools); + _role = that._role; +} + +void ShardsPool::_init_pool(const Shards &shards) { + for (const auto &shard : shards) { + _add_node(shard.second); + } +} + +Shards ShardsPool::_cluster_slots(Connection &connection) const { + auto reply = _cluster_slots_command(connection); + + assert(reply); + + return _parse_reply(*reply); +} + +ReplyUPtr ShardsPool::_cluster_slots_command(Connection &connection) const { + connection.send("CLUSTER SLOTS"); + + return connection.recv(); +} + +Shards ShardsPool::_parse_reply(redisReply &reply) const { + if (!reply::is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.element == nullptr || reply.elements == 0) { + throw Error("Empty slots"); + } + + Shards shards; + for (std::size_t idx = 0; idx != reply.elements; ++idx) { + auto *sub_reply = reply.element[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null slot info"); + } + + shards.emplace(_parse_slot_info(*sub_reply)); + } + + return shards; +} + +Slot ShardsPool::_parse_slot(redisReply *reply) const { + if (reply == nullptr) { + throw ProtoError("null slot id"); + } + + auto slot = reply::parse(*reply); + if (slot < 0) { + throw ProtoError("negative slot id"); + } + + return static_cast(slot); +} + +Node ShardsPool::_parse_node(redisReply *reply) const { + if (reply == nullptr + || !reply::is_array(*reply) + || reply->element == nullptr + || reply->elements < 2) { + throw ProtoError("invalid node info"); + } + + auto host = reply::parse(*(reply->element[0])); + int port = reply::parse(*(reply->element[1])); + + return {host, port}; +} + +std::pair ShardsPool::_parse_slot_info(redisReply &reply) const { + // Slot info is an array reply: min slot, max slot, master node, [slave nodes] + if (reply.elements < 3 || reply.element == nullptr) { + throw ProtoError("Invalid slot info"); + } + + auto min_slot = _parse_slot(reply.element[0]); + + auto max_slot = _parse_slot(reply.element[1]); + + if (min_slot > max_slot) { + throw ProtoError("Invalid slot range"); + } + + auto slot_range = SlotRange{min_slot, max_slot}; + + switch (_role) { + case Role::MASTER: + // Return master node, i.e. `reply.element[2]`. + return std::make_pair(slot_range, _parse_node(reply.element[2])); + + case Role::SLAVE: { + auto size = reply.elements; + if (size <= 3) { + throw Error("no slave node available"); + } + + // Randomly pick a slave node. + auto *slave_node_reply = reply.element[_random(3, size - 1)]; + + return std::make_pair(slot_range, _parse_node(slave_node_reply)); + } + + default: + throw Error("unknown role"); + } +} + +Slot ShardsPool::_slot(const StringView &key) const { + // The following code is copied from: https://redis.io/topics/cluster-spec + // And I did some minor changes. + + const auto *k = key.data(); + auto keylen = key.size(); + + // start-end indexes of { and }. + std::size_t s = 0; + std::size_t e = 0; + + // Search the first occurrence of '{'. + for (s = 0; s < keylen; s++) + if (k[s] == '{') break; + + // No '{' ? Hash the whole key. This is the base case. + if (s == keylen) return crc16(k, keylen) & SHARDS; + + // '{' found? Check if we have the corresponding '}'. + for (e = s + 1; e < keylen; e++) + if (k[e] == '}') break; + + // No '}' or nothing between {} ? Hash the whole key. + if (e == keylen || e == s + 1) return crc16(k, keylen) & SHARDS; + + // If we are here there is both a { and a } on its right. Hash + // what is in the middle between { and }. + return crc16(k + s + 1, e - s - 1) & SHARDS; +} + +Slot ShardsPool::_slot() const { + return _random(0, SHARDS); +} + +std::size_t ShardsPool::_random(std::size_t min, std::size_t max) const { + static thread_local std::default_random_engine engine; + + std::uniform_int_distribution uniform_dist(min, max); + + return uniform_dist(engine); +} + +ConnectionPoolSPtr& ShardsPool::_get_pool(Slot slot) { + auto shards_iter = _shards.lower_bound(SlotRange{slot, slot}); + if (shards_iter == _shards.end() || slot < shards_iter->first.min) { + throw Error("Slot is out of range: " + std::to_string(slot)); + } + + const auto &node = shards_iter->second; + + auto node_iter = _pools.find(node); + if (node_iter == _pools.end()) { + throw Error("Slot is NOT covered: " + std::to_string(slot)); + } + + return node_iter->second; +} + +ConnectionPoolSPtr ShardsPool::_fetch(Slot slot) { + std::lock_guard lock(_mutex); + + return _get_pool(slot); +} + +ConnectionOptions ShardsPool::_connection_options(Slot slot) { + std::lock_guard lock(_mutex); + + auto &pool = _get_pool(slot); + + assert(pool); + + return pool->connection_options(); +} + +auto ShardsPool::_add_node(const Node &node) -> NodeMap::iterator { + auto opts = _connection_opts; + opts.host = node.host; + opts.port = node.port; + + // TODO: Better set readonly an attribute of `Node`. + if (_role == Role::SLAVE) { + opts.readonly = true; + } + + return _pools.emplace(node, std::make_shared(_pool_opts, opts)).first; +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards_pool.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards_pool.h new file mode 100644 index 000000000..e6e9e4a52 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/shards_pool.h @@ -0,0 +1,121 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H +#define SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H + +#include +#include +#include +#include +#include +#include "reply.h" +#include "connection_pool.h" +#include "shards.h" + +namespace sw { + +namespace redis { + +class ShardsPool { +public: + ShardsPool() = default; + + ShardsPool(const ShardsPool &that) = delete; + ShardsPool& operator=(const ShardsPool &that) = delete; + + ShardsPool(ShardsPool &&that); + ShardsPool& operator=(ShardsPool &&that); + + ~ShardsPool() = default; + + ShardsPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts, + Role role); + + // Fetch a connection by key. + ConnectionPoolSPtr fetch(const StringView &key); + + // Randomly pick a connection. + ConnectionPoolSPtr fetch(); + + // Fetch a connection by node. + ConnectionPoolSPtr fetch(const Node &node); + + void update(); + + ConnectionOptions connection_options(const StringView &key); + + ConnectionOptions connection_options(); + + Shards shards(); + +private: + void _move(ShardsPool &&that); + + void _init_pool(const Shards &shards); + + Shards _cluster_slots(Connection &connection) const; + + ReplyUPtr _cluster_slots_command(Connection &connection) const; + + Shards _parse_reply(redisReply &reply) const; + + Slot _parse_slot(redisReply *reply) const; + + Node _parse_node(redisReply *reply) const; + + std::pair _parse_slot_info(redisReply &reply) const; + + // Get slot by key. + std::size_t _slot(const StringView &key) const; + + // Randomly pick a slot. + std::size_t _slot() const; + + // Get a random number between [min, max] + std::size_t _random(std::size_t min, std::size_t max) const; + + ConnectionPoolSPtr& _get_pool(Slot slot); + + ConnectionPoolSPtr _fetch(Slot slot); + + ConnectionOptions _connection_options(Slot slot); + + using NodeMap = std::unordered_map; + + NodeMap::iterator _add_node(const Node &node); + + ConnectionPoolOptions _pool_opts; + + ConnectionOptions _connection_opts; + + Shards _shards; + + NodeMap _pools; + + std::mutex _mutex; + + Role _role = Role::MASTER; + + static const std::size_t SHARDS = 16383; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/subscriber.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/subscriber.cpp new file mode 100644 index 000000000..b699b02f0 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/subscriber.cpp @@ -0,0 +1,222 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "subscriber.h" +#include + +namespace sw { + +namespace redis { + +const Subscriber::TypeIndex Subscriber::_msg_type_index = { + {"message", MsgType::MESSAGE}, + {"pmessage", MsgType::PMESSAGE}, + {"subscribe", MsgType::SUBSCRIBE}, + {"unsubscribe", MsgType::UNSUBSCRIBE}, + {"psubscribe", MsgType::PSUBSCRIBE}, + {"punsubscribe", MsgType::PUNSUBSCRIBE} +}; + +Subscriber::Subscriber(Connection connection) : _connection(std::move(connection)) {} + +void Subscriber::subscribe(const StringView &channel) { + _check_connection(); + + // TODO: cmd::subscribe DOES NOT send the subscribe message to Redis. + // In fact, it puts the command to network buffer. + // So we need a queue to record these sub or unsub commands, and + // ensure that before stopping the subscriber, all these commands + // have really been sent to Redis. + cmd::subscribe(_connection, channel); +} + +void Subscriber::unsubscribe() { + _check_connection(); + + cmd::unsubscribe(_connection); +} + +void Subscriber::unsubscribe(const StringView &channel) { + _check_connection(); + + cmd::unsubscribe(_connection, channel); +} + +void Subscriber::psubscribe(const StringView &pattern) { + _check_connection(); + + cmd::psubscribe(_connection, pattern); +} + +void Subscriber::punsubscribe() { + _check_connection(); + + cmd::punsubscribe(_connection); +} + +void Subscriber::punsubscribe(const StringView &pattern) { + _check_connection(); + + cmd::punsubscribe(_connection, pattern); +} + +void Subscriber::consume() { + _check_connection(); + + ReplyUPtr reply; + try { + reply = _connection.recv(); + } catch (const TimeoutError &) { + _connection.reset(); + throw; + } + + assert(reply); + + if (!reply::is_array(*reply) || reply->elements < 1 || reply->element == nullptr) { + throw ProtoError("Invalid subscribe message"); + } + + auto type = _msg_type(reply->element[0]); + switch (type) { + case MsgType::MESSAGE: + _handle_message(*reply); + break; + + case MsgType::PMESSAGE: + _handle_pmessage(*reply); + break; + + case MsgType::SUBSCRIBE: + case MsgType::UNSUBSCRIBE: + case MsgType::PSUBSCRIBE: + case MsgType::PUNSUBSCRIBE: + _handle_meta(type, *reply); + break; + + default: + assert(false); + } +} + +Subscriber::MsgType Subscriber::_msg_type(redisReply *reply) const { + if (reply == nullptr) { + throw ProtoError("Null type reply."); + } + + auto type = reply::parse(*reply); + + auto iter = _msg_type_index.find(type); + if (iter == _msg_type_index.end()) { + throw ProtoError("Invalid message type."); + } + + return iter->second; +} + +void Subscriber::_check_connection() { + if (_connection.broken()) { + throw Error("Connection is broken"); + } +} + +void Subscriber::_handle_message(redisReply &reply) { + if (_msg_callback == nullptr) { + return; + } + + if (reply.elements != 3) { + throw ProtoError("Expect 3 sub replies"); + } + + assert(reply.element != nullptr); + + auto *channel_reply = reply.element[1]; + if (channel_reply == nullptr) { + throw ProtoError("Null channel reply"); + } + auto channel = reply::parse(*channel_reply); + + auto *msg_reply = reply.element[2]; + if (msg_reply == nullptr) { + throw ProtoError("Null message reply"); + } + auto msg = reply::parse(*msg_reply); + + _msg_callback(std::move(channel), std::move(msg)); +} + +void Subscriber::_handle_pmessage(redisReply &reply) { + if (_pmsg_callback == nullptr) { + return; + } + + if (reply.elements != 4) { + throw ProtoError("Expect 4 sub replies"); + } + + assert(reply.element != nullptr); + + auto *pattern_reply = reply.element[1]; + if (pattern_reply == nullptr) { + throw ProtoError("Null pattern reply"); + } + auto pattern = reply::parse(*pattern_reply); + + auto *channel_reply = reply.element[2]; + if (channel_reply == nullptr) { + throw ProtoError("Null channel reply"); + } + auto channel = reply::parse(*channel_reply); + + auto *msg_reply = reply.element[3]; + if (msg_reply == nullptr) { + throw ProtoError("Null message reply"); + } + auto msg = reply::parse(*msg_reply); + + _pmsg_callback(std::move(pattern), std::move(channel), std::move(msg)); +} + +void Subscriber::_handle_meta(MsgType type, redisReply &reply) { + if (_meta_callback == nullptr) { + return; + } + + if (reply.elements != 3) { + throw ProtoError("Expect 3 sub replies"); + } + + assert(reply.element != nullptr); + + auto *channel_reply = reply.element[1]; + if (channel_reply == nullptr) { + throw ProtoError("Null channel reply"); + } + auto channel = reply::parse(*channel_reply); + + auto *num_reply = reply.element[2]; + if (num_reply == nullptr) { + throw ProtoError("Null num reply"); + } + auto num = reply::parse(*num_reply); + + _meta_callback(type, std::move(channel), num); +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/subscriber.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/subscriber.h new file mode 100644 index 000000000..8b7c5cfb4 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/subscriber.h @@ -0,0 +1,231 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H +#define SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H + +#include +#include +#include +#include "connection.h" +#include "reply.h" +#include "command.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +// @NOTE: Subscriber is NOT thread-safe. +// Subscriber uses callbacks to handle messages. There are 6 kinds of messages: +// 1) MESSAGE: message sent to a channel. +// 2) PMESSAGE: message sent to channels of a given pattern. +// 3) SUBSCRIBE: meta message sent when we successfully subscribe to a channel. +// 4) UNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel. +// 5) PSUBSCRIBE: meta message sent when we successfully subscribe to a channel pattern. +// 6) PUNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel pattern. +// +// Use Subscriber::on_message(MsgCallback) to set the callback function for message of +// *MESSAGE* type, and the callback interface is: +// void (std::string channel, std::string msg) +// +// Use Subscriber::on_pmessage(PatternMsgCallback) to set the callback function for message of +// *PMESSAGE* type, and the callback interface is: +// void (std::string pattern, std::string channel, std::string msg) +// +// Messages of other types are called *META MESSAGE*, they have the same callback interface. +// Use Subscriber::on_meta(MetaCallback) to set the callback function: +// void (Subscriber::MsgType type, OptionalString channel, long long num) +// +// NOTE: If we haven't subscribe/psubscribe to any channel/pattern, and try to +// unsubscribe/punsubscribe without any parameter, i.e. unsubscribe/punsubscribe all +// channels/patterns, *channel* will be null. So the second parameter of meta callback +// is of type *OptionalString*. +// +// All these callback interfaces pass std::string by value, and you can take their ownership +// (i.e. std::move) safely. +// +// If you don't set callback for a specific kind of message, Subscriber::consume() will +// receive the message, and ignore it, i.e. no callback will be called. +class Subscriber { +public: + Subscriber(const Subscriber &) = delete; + Subscriber& operator=(const Subscriber &) = delete; + + Subscriber(Subscriber &&) = default; + Subscriber& operator=(Subscriber &&) = default; + + ~Subscriber() = default; + + enum class MsgType { + SUBSCRIBE, + UNSUBSCRIBE, + PSUBSCRIBE, + PUNSUBSCRIBE, + MESSAGE, + PMESSAGE + }; + + template + void on_message(MsgCb msg_callback); + + template + void on_pmessage(PMsgCb pmsg_callback); + + template + void on_meta(MetaCb meta_callback); + + void subscribe(const StringView &channel); + + template + void subscribe(Input first, Input last); + + template + void subscribe(std::initializer_list channels) { + subscribe(channels.begin(), channels.end()); + } + + void unsubscribe(); + + void unsubscribe(const StringView &channel); + + template + void unsubscribe(Input first, Input last); + + template + void unsubscribe(std::initializer_list channels) { + unsubscribe(channels.begin(), channels.end()); + } + + void psubscribe(const StringView &pattern); + + template + void psubscribe(Input first, Input last); + + template + void psubscribe(std::initializer_list channels) { + psubscribe(channels.begin(), channels.end()); + } + + void punsubscribe(); + + void punsubscribe(const StringView &channel); + + template + void punsubscribe(Input first, Input last); + + template + void punsubscribe(std::initializer_list channels) { + punsubscribe(channels.begin(), channels.end()); + } + + void consume(); + +private: + friend class Redis; + + friend class RedisCluster; + + explicit Subscriber(Connection connection); + + MsgType _msg_type(redisReply *reply) const; + + void _check_connection(); + + void _handle_message(redisReply &reply); + + void _handle_pmessage(redisReply &reply); + + void _handle_meta(MsgType type, redisReply &reply); + + using MsgCallback = std::function; + + using PatternMsgCallback = std::function; + + using MetaCallback = std::function; + + using TypeIndex = std::unordered_map; + static const TypeIndex _msg_type_index; + + Connection _connection; + + MsgCallback _msg_callback = nullptr; + + PatternMsgCallback _pmsg_callback = nullptr; + + MetaCallback _meta_callback = nullptr; +}; + +template +void Subscriber::on_message(MsgCb msg_callback) { + _msg_callback = msg_callback; +} + +template +void Subscriber::on_pmessage(PMsgCb pmsg_callback) { + _pmsg_callback = pmsg_callback; +} + +template +void Subscriber::on_meta(MetaCb meta_callback) { + _meta_callback = meta_callback; +} + +template +void Subscriber::subscribe(Input first, Input last) { + if (first == last) { + return; + } + + _check_connection(); + + cmd::subscribe_range(_connection, first, last); +} + +template +void Subscriber::unsubscribe(Input first, Input last) { + _check_connection(); + + cmd::unsubscribe_range(_connection, first, last); +} + +template +void Subscriber::psubscribe(Input first, Input last) { + if (first == last) { + return; + } + + _check_connection(); + + cmd::psubscribe_range(_connection, first, last); +} + +template +void Subscriber::punsubscribe(Input first, Input last) { + _check_connection(); + + cmd::punsubscribe_range(_connection, first, last); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/tls/tls.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/tls/tls.cpp new file mode 100644 index 000000000..1ee4309dc --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/tls/tls.cpp @@ -0,0 +1,72 @@ +/************************************************************************** + Copyright (c) 2020 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "tls.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +namespace tls { + +bool& auto_init() { + static bool init = true; + + return init; +} + +void disable_auto_init() { + auto_init() = false; +} + +TlsInit::TlsInit() { + if (auto_init()) { + redisInitOpenSSL(); + } +} + +TlsContextUPtr secure_connection(redisContext &ctx, const TlsOptions &opts) { + static TlsInit tls_init; + + auto c_str = [](const std::string &s) { + return s.empty() ? nullptr : s.c_str(); + }; + + redisSSLContextError err; + auto tls_ctx = TlsContextUPtr(redisCreateSSLContext(c_str(opts.cacert), + c_str(opts.cacertdir), + c_str(opts.cert), + c_str(opts.key), + c_str(opts.sni), + &err)); + if (!tls_ctx) { + throw Error(std::string("failed to create TLS context: ") + + redisSSLContextGetError(err)); + } + + if (redisInitiateSSLWithContext(&ctx, tls_ctx.get()) != REDIS_OK) { + throw_error(ctx, "Failed to initialize TLS connection"); + } + + return tls_ctx; +} + +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/tls/tls.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/tls/tls.h new file mode 100644 index 000000000..7b586c24d --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/tls/tls.h @@ -0,0 +1,77 @@ +/************************************************************************** + Copyright (c) 2020 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TLS_H +#define SEWENEW_REDISPLUSPLUS_TLS_H + +#include +#include +#include +#include + +namespace sw { + +namespace redis { + +namespace tls { + +// Disable auto initializing OpenSSL. +// You should call it only once and call it before any sw::redis::Redis operation. +// Otherwise, the behavior is undefined. +void disable_auto_init(); + +class TlsInit { +public: + TlsInit(); +}; + +struct TlsOptions { + bool enabled = false; + + std::string cacert; + + std::string cacertdir; + + std::string cert; + + std::string key; + + std::string sni; +}; + +inline bool enabled(const TlsOptions &opts) { + return opts.enabled; +} + +struct TlsContextDeleter { + void operator()(redisSSLContext *ssl) const { + if (ssl != nullptr) { + redisFreeSSLContext(ssl); + } + } +}; + +using TlsContextUPtr = std::unique_ptr; + +TlsContextUPtr secure_connection(redisContext &ctx, const TlsOptions &opts); + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TLS_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/transaction.cpp b/ext/redis-plus-plus-1.3.3/src/sw/redis++/transaction.cpp new file mode 100644 index 000000000..faa1bd178 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/transaction.cpp @@ -0,0 +1,123 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#include "transaction.h" +#include "command.h" + +namespace sw { + +namespace redis { + +std::vector TransactionImpl::exec(Connection &connection, std::size_t cmd_num) { + _close_transaction(); + + _get_queued_replies(connection, cmd_num); + + return _exec(connection); +} + +void TransactionImpl::discard(Connection &connection, std::size_t cmd_num) { + _close_transaction(); + + _get_queued_replies(connection, cmd_num); + + _discard(connection); +} + +void TransactionImpl::_open_transaction(Connection &connection) { + assert(!_in_transaction); + + cmd::multi(connection); + auto reply = connection.recv(); + auto status = reply::to_status(*reply); + if (status != "OK") { + throw Error("Failed to open transaction: " + status); + } + + _in_transaction = true; +} + +void TransactionImpl::_close_transaction() { + if (!_in_transaction) { + throw Error("No command in transaction"); + } + + _in_transaction = false; +} + +void TransactionImpl::_get_queued_reply(Connection &connection) { + auto reply = connection.recv(); + auto status = reply::to_status(*reply); + if (status != "QUEUED") { + throw Error("Invalid QUEUED reply: " + status); + } +} + +void TransactionImpl::_get_queued_replies(Connection &connection, std::size_t cmd_num) { + if (_piped) { + // Get all QUEUED reply + while (cmd_num > 0) { + _get_queued_reply(connection); + + --cmd_num; + } + } +} + +std::vector TransactionImpl::_exec(Connection &connection) { + cmd::exec(connection); + + auto reply = connection.recv(); + + if (reply::is_nil(*reply)) { + // Execution has been aborted, i.e. watched key has been modified. + throw WatchError(); + } + + if (!reply::is_array(*reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply->element == nullptr || reply->elements == 0) { + // Since we don't allow EXEC without any command, this ARRAY reply + // should NOT be null or empty. + throw ProtoError("Null ARRAY reply"); + } + + std::vector replies; + for (std::size_t idx = 0; idx != reply->elements; ++idx) { + auto *sub_reply = reply->element[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null sub reply"); + } + + auto r = ReplyUPtr(sub_reply); + reply->element[idx] = nullptr; + replies.push_back(std::move(r)); + } + + return replies; +} + +void TransactionImpl::_discard(Connection &connection) { + cmd::discard(connection); + auto reply = connection.recv(); + reply::parse(*reply); +} + +} + +} diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/transaction.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/transaction.h new file mode 100644 index 000000000..f19f24889 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/transaction.h @@ -0,0 +1,77 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TRANSACTION_H +#define SEWENEW_REDISPLUSPLUS_TRANSACTION_H + +#include +#include +#include "connection.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class TransactionImpl { +public: + explicit TransactionImpl(bool piped) : _piped(piped) {} + + template + void command(Connection &connection, Cmd cmd, Args &&...args); + + std::vector exec(Connection &connection, std::size_t cmd_num); + + void discard(Connection &connection, std::size_t cmd_num); + +private: + void _open_transaction(Connection &connection); + + void _close_transaction(); + + void _get_queued_reply(Connection &connection); + + void _get_queued_replies(Connection &connection, std::size_t cmd_num); + + std::vector _exec(Connection &connection); + + void _discard(Connection &connection); + + bool _in_transaction = false; + + bool _piped; +}; + +template +void TransactionImpl::command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + if (!_in_transaction) { + _open_transaction(connection); + } + + cmd(connection, std::forward(args)...); + + if (!_piped) { + _get_queued_reply(connection); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TRANSACTION_H diff --git a/ext/redis-plus-plus-1.3.3/src/sw/redis++/utils.h b/ext/redis-plus-plus-1.3.3/src/sw/redis++/utils.h new file mode 100644 index 000000000..f77f796d6 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/src/sw/redis++/utils.h @@ -0,0 +1,193 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_UTILS_H +#define SEWENEW_REDISPLUSPLUS_UTILS_H + +#include +#include +#include +#include "cxx_utils.h" + +namespace sw { + +namespace redis { + +using OptionalString = Optional; + +using OptionalLongLong = Optional; + +using OptionalDouble = Optional; + +using OptionalStringPair = Optional>; + +template +struct IsKvPair : std::false_type {}; + +template +struct IsKvPair> : std::true_type {}; + +template +using Void = void; + +template > +struct IsInserter : std::false_type {}; + +template +//struct IsInserter> : std::true_type {}; +struct IsInserter::value>::type> + : std::true_type {}; + +template > +struct IterType { + using type = typename std::iterator_traits::value_type; +}; + +template +//struct IterType> { +struct IterType::value>::type> { + typename std::enable_if::value>::type> { + using type = typename std::decay::type; +}; + +template > +struct IsIter : std::false_type {}; + +template +struct IsIter::value>::type> : std::true_type {}; + +template +//struct IsIter::iterator_category>> +struct IsIter::value_type>::value>::type> + : std::integral_constant::value> {}; + +template +struct IsKvPairIter : IsKvPair::type> {}; + +template +struct TupleWithType : std::false_type {}; + +template +struct TupleWithType> : std::false_type {}; + +template +struct TupleWithType> : TupleWithType> {}; + +template +struct TupleWithType> : std::true_type {}; + +template +struct IndexSequence {}; + +template +struct MakeIndexSequence : MakeIndexSequence {}; + +template +struct MakeIndexSequence<0, Is...> : IndexSequence {}; + +// NthType and NthValue are taken from +// https://stackoverflow.com/questions/14261183 +template +struct NthType {}; + +template +struct NthType<0, Arg, Args...> { + using type = Arg; +}; + +template +struct NthType { + using type = typename NthType::type; +}; + +template +struct LastType { + using type = typename NthType::type; +}; + +struct InvalidLastType {}; + +template <> +struct LastType<> { + using type = InvalidLastType; +}; + +template +auto NthValue(Arg &&arg, Args &&...) + -> typename std::enable_if<(I == 0), decltype(std::forward(arg))>::type { + return std::forward(arg); +} + +template +auto NthValue(Arg &&, Args &&...args) + -> typename std::enable_if<(I > 0), + decltype(std::forward::type>( + std::declval::type>()))>::type { + return std::forward::type>( + NthValue(std::forward(args)...)); +} + +template +auto LastValue(Args &&...args) + -> decltype(std::forward::type>( + std::declval::type>())) { + return std::forward::type>( + NthValue(std::forward(args)...)); +} + +template > +struct HasPushBack : std::false_type {}; + +template +struct HasPushBack().push_back(std::declval()) + )>::value>::type> : std::true_type {}; + +template > +struct HasInsert : std::false_type {}; + +template +struct HasInsert().insert(std::declval(), + std::declval())), + typename T::iterator>::value>::type> : std::true_type {}; + +template +struct IsSequenceContainer + : std::integral_constant::value + && !std::is_same::type, std::string>::value> {}; + +template +struct IsAssociativeContainer + : std::integral_constant::value && !HasPushBack::value> {}; + +uint16_t crc16(const char *buf, int len); + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_UTILS_H diff --git a/ext/redis-plus-plus-1.3.3/test/CMakeLists.txt b/ext/redis-plus-plus-1.3.3/test/CMakeLists.txt new file mode 100644 index 000000000..92219b6be --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/CMakeLists.txt @@ -0,0 +1,58 @@ +project(test_redis++) + +cmake_minimum_required(VERSION 3.1) + +set(REDIS_PLUS_PLUS_TEST_SOURCES src/sw/redis++/test_main.cpp) + +add_executable(${PROJECT_NAME} ${REDIS_PLUS_PLUS_TEST_SOURCES}) + +# hiredis dependency +find_path(HIREDIS_HEADER hiredis REQUIRED) +target_include_directories(${PROJECT_NAME} PRIVATE $) + +find_library(TEST_HIREDIS_LIB libhiredis.a) +if(NOT TEST_HIREDIS_LIB) + find_library(TEST_HIREDIS_LIB libhiredis_static.a) + if(NOT TEST_HIREDIS_LIB) + find_library(TEST_HIREDIS_LIB hiredis) + endif() +endif() +target_link_libraries(${PROJECT_NAME} ${TEST_HIREDIS_LIB}) + +if(REDIS_PLUS_PLUS_USE_TLS) + find_package(OpenSSL REQUIRED) + find_library(TEST_HIREDIS_TLS_LIB libhiredis_ssl.a) + if(NOT TEST_HIREDIS_TLS_LIB) + find_library(TEST_HIREDIS_TLS_LIB libhiredis_ssl_static.a) + if(NOT TEST_HIREDIS_TLS_LIB) + find_library(TEST_HIREDIS_TLS_LIB hiredis_ssl) + endif() + endif() + target_link_libraries(${PROJECT_NAME} ${TEST_HIREDIS_TLS_LIB} ${OPENSSL_LIBRARIES}) +endif() + +# redis++ dependency +target_include_directories(${PROJECT_NAME} PRIVATE + $ + $ + $) + +# solaris socket dependency +if(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + target_link_libraries(${PROJECT_NAME} -lsocket) +endif(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + +# Windows socket dependency +if(WIN32) + target_link_libraries(${PROJECT_NAME} ws2_32) +endif() + +find_package(Threads REQUIRED) + +if(REDIS_PLUS_PLUS_BUILD_STATIC) + set(REDIS_PLUS_PLUS_LIB redis++_static) +else() + set(REDIS_PLUS_PLUS_LIB redis++) +endif() + +target_link_libraries(${PROJECT_NAME} ${REDIS_PLUS_PLUS_LIB} ${CMAKE_THREAD_LIBS_INIT}) diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/benchmark_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/benchmark_test.h new file mode 100644 index 000000000..309bc6863 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/benchmark_test.h @@ -0,0 +1,83 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +struct BenchmarkOptions { + std::size_t pool_size = 5; + std::size_t thread_num = 10; + std::size_t total_request_num = 100000; + std::size_t key_len = 10; + std::size_t val_len = 10; +}; + +template +class BenchmarkTest { +public: + BenchmarkTest(const BenchmarkOptions &opts, RedisInstance &instance); + + ~BenchmarkTest() { + _cleanup(); + } + + void run(); + +private: + template + void _run(const std::string &title, Func &&func); + + template + std::size_t _run(Func &&func, std::size_t request_num); + + void _test_get(); + + std::vector _gen_keys() const; + + std::string _gen_value() const; + + void _cleanup(); + + const std::string& _key(std::size_t idx) const { + return _keys[idx % _keys.size()]; + } + + BenchmarkOptions _opts; + + RedisInstance &_redis; + + std::vector _keys; + + std::string _value; +}; + +} + +} + +} + +#include "benchmark_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/benchmark_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/benchmark_test.hpp new file mode 100644 index 000000000..eaf50ec50 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/benchmark_test.hpp @@ -0,0 +1,178 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP + +#include +#include +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +BenchmarkTest::BenchmarkTest(const BenchmarkOptions &opts, + RedisInstance &instance) : _opts(opts), _redis(instance) { + REDIS_ASSERT(_opts.pool_size > 0 + && _opts.thread_num > 0 + && _opts.total_request_num > 0 + && _opts.key_len > 0 + && _opts.val_len > 0, + "Invalid benchmark test options."); + + _keys = _gen_keys(); + _value = _gen_value(); +} + +template +void BenchmarkTest::run() { + _cleanup(); + + _run("SET key value", [this](std::size_t idx) { this->_redis.set(this->_key(idx), _value); }); + + _run("GET key", [this](std::size_t idx) { + auto res = this->_redis.get(this->_key(idx)); + (void)res; + }); + + _cleanup(); + + _run("LPUSH key value", [this](std::size_t idx) { + this->_redis.lpush(this->_key(idx), _value); + }); + + _run("LRANGE key 0 10", [this](std::size_t idx) { + std::vector res; + res.reserve(10); + this->_redis.lrange(this->_key(idx), 0, 10, std::back_inserter(res)); + }); + + _run("LPOP key", [this](std::size_t idx) { + auto res = this->_redis.lpop(this->_key(idx)); + (void)res; + }); + + _cleanup(); + + _run("INCR key", [this](std::size_t idx) { + auto num = this->_redis.incr(this->_key(idx)); + (void)num; + }); + + _cleanup(); + + _run("SADD key member", [this](std::size_t idx) { + auto num = this->_redis.sadd(this->_key(idx), _value); + (void)num; + }); + + _run("SPOP key", [this](std::size_t idx) { + auto res = this->_redis.spop(this->_key(idx)); + (void)res; + }); + + _cleanup(); +} + +template +template +void BenchmarkTest::_run(const std::string &title, Func &&func) { + auto thread_num = _opts.thread_num; + auto requests_per_thread = _opts.total_request_num / thread_num; + auto total_request_num = requests_per_thread * thread_num; + std::vector> res; + res.reserve(thread_num); + res.push_back(std::async(std::launch::async, + [this](Func &&func, std::size_t request_num) { + return this->_run(std::forward(func), request_num); + }, + std::forward(func), + requests_per_thread)); + + auto total_in_msec = 0; + for (auto &fut : res) { + total_in_msec += fut.get(); + } + + auto total_in_sec = total_in_msec * 1.0 / 1000; + + auto avg = total_in_msec * 1.0 / total_request_num; + + auto ops = static_cast(1000 / avg); + + std::cout << "-----" << title << "-----" << std::endl; + std::cout << total_request_num << " requests cost " << total_in_sec << " seconds" << std::endl; + std::cout << ops << " requests per second" << std::endl; +} + +template +template +std::size_t BenchmarkTest::_run(Func &&func, std::size_t request_num) { + auto start = std::chrono::steady_clock::now(); + + for (auto idx = 0U; idx != request_num; ++idx) { + func(idx); + } + + auto stop = std::chrono::steady_clock::now(); + + return std::chrono::duration_cast(stop - start).count(); +} + +template +std::vector BenchmarkTest::_gen_keys() const { + const auto KEY_NUM = 100; + std::vector res; + res.reserve(KEY_NUM); + std::default_random_engine engine(std::random_device{}()); + std::uniform_int_distribution uniform_dist(0, 255); + for (auto i = 0; i != KEY_NUM; ++i) { + std::string str; + str.reserve(_opts.key_len); + for (std::size_t j = 0; j != _opts.key_len; ++j) { + str.push_back(static_cast(uniform_dist(engine))); + } + res.push_back(str); + } + + return res; +} + +template +std::string BenchmarkTest::_gen_value() const { + return std::string(_opts.val_len, 'x'); +} + +template +void BenchmarkTest::_cleanup() { + for (const auto &key : _keys) { + _redis.del(key); + } +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/connection_cmds_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/connection_cmds_test.h new file mode 100644 index 000000000..2127257c3 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/connection_cmds_test.h @@ -0,0 +1,49 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class ConnectionCmdTest { +public: + explicit ConnectionCmdTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + void _run(Redis &redis); + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "connection_cmds_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/connection_cmds_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/connection_cmds_test.hpp new file mode 100644 index 000000000..90e7c313b --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/connection_cmds_test.hpp @@ -0,0 +1,50 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP + +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void ConnectionCmdTest::run() { + cluster_specializing_test(*this, &ConnectionCmdTest::_run, _redis); +} + +template +void ConnectionCmdTest::_run(Redis &instance) { + auto message = std::string("hello"); + + REDIS_ASSERT(instance.echo(message) == message, "failed to test echo"); + + REDIS_ASSERT(instance.ping() == "PONG", "failed to test ping"); + + REDIS_ASSERT(instance.ping(message) == message, "failed to test ping"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/geo_cmds_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/geo_cmds_test.h new file mode 100644 index 000000000..41b9795fa --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/geo_cmds_test.h @@ -0,0 +1,47 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class GeoCmdTest { +public: + explicit GeoCmdTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + RedisInstance &_redis; +}; + +} + +} + +} + +#include "geo_cmds_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/geo_cmds_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/geo_cmds_test.hpp new file mode 100644 index 000000000..f74d2166c --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/geo_cmds_test.hpp @@ -0,0 +1,187 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP + +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void GeoCmdTest::run() { + auto key = test_key("geo"); + auto not_exist_key = test_key("geo_not_exist"); + auto dest = test_key("dest"); + + KeyDeleter deleter(_redis, {key, dest}); + + auto members = { + std::make_tuple("m1", 10.0, 11.0), + std::make_tuple("m2", 10.1, 11.1), + std::make_tuple("m3", 10.2, 11.2) + }; + + REDIS_ASSERT(_redis.geoadd(key, std::make_tuple("m1", 10.0, 11.0)) == 1, + "failed to test geoadd"); + REDIS_ASSERT(_redis.geoadd(key, members) == 2, "failed to test geoadd"); + + auto dist = _redis.geodist(key, "m1", "m4", GeoUnit::KM); + REDIS_ASSERT(!dist, "failed to test geodist with nonexistent member"); + + auto hash = _redis.geohash(key, "m1"); + REDIS_ASSERT(bool(hash) && *hash == "s1zned3z8u0", "failed to test geohash"); + hash = _redis.geohash(key, "m9"); + REDIS_ASSERT(!hash, "failed to test geohash"); + + std::vector hashes; + _redis.geohash(key, {"m1", "m4"}, std::back_inserter(hashes)); + REDIS_ASSERT(hashes.size() == 2, "failed to test geohash"); + REDIS_ASSERT(bool(hashes[0]) && *(hashes[0]) == "s1zned3z8u0" && !(hashes[1]), + "failed to test geohash"); + hashes.clear(); + _redis.geohash(key, {"m4"}, std::back_inserter(hashes)); + REDIS_ASSERT(hashes.size() == 1 && !(hashes[0]), "failed to test geohash"); + + std::vector>> pos; + _redis.geopos(key, {"m4"}, std::back_inserter(pos)); + REDIS_ASSERT(pos.size() == 1 && !(pos[0]), "failed to test geopos"); + + auto position = _redis.geopos(key, "m3"); + REDIS_ASSERT(bool(position), "failed to test geopos"); + position = _redis.geopos(key, "m4"); + REDIS_ASSERT(!position, "failed to test geopos"); + + auto num = _redis.georadius(key, + std::make_pair(10.1, 11.1), + 100, + GeoUnit::KM, + dest, + false, + 10); + REDIS_ASSERT(bool(num) && *num == 3, "failed to test georadius with store option"); + + num = _redis.georadius(key, + std::make_pair(50, 50), + 1, + GeoUnit::M, + dest, + false, + 10); + REDIS_ASSERT(bool(num) && *num == 0, "failed to test georadius with store option"); + + num = _redis.georadius(not_exist_key, + std::make_pair(10.1, 11.1), + 100, + GeoUnit::KM, + dest, + false, + 10); + REDIS_ASSERT(!num || (num && *num == 0), "failed to test georadius with store option"); + + std::vector mems; + _redis.georadius(key, + std::make_pair(10.1, 11.1), + 100, + GeoUnit::KM, + 10, + true, + std::back_inserter(mems)); + REDIS_ASSERT(mems.size() == 3, "failed to test georadius with no option"); + + std::vector> with_dist; + _redis.georadius(key, + std::make_pair(10.1, 11.1), + 100, + GeoUnit::KM, + 10, + true, + std::back_inserter(with_dist)); + REDIS_ASSERT(with_dist.size() == 3, "failed to test georadius with dist"); + + std::vector>> with_dist_coord; + _redis.georadius(key, + std::make_pair(10.1, 11.1), + 100, + GeoUnit::KM, + 10, + true, + std::back_inserter(with_dist_coord)); + REDIS_ASSERT(with_dist_coord.size() == 3, "failed to test georadius with dist and coord"); + + num = _redis.georadiusbymember(key, + "m1", + 100, + GeoUnit::KM, + dest, + false, + 10); + REDIS_ASSERT(bool(num) && *num == 3, "failed to test georadiusbymember with store option"); + + num = _redis.georadiusbymember(not_exist_key, + "m1", + 100, + GeoUnit::KM, + dest, + false, + 10); + REDIS_ASSERT(!num || (num && *num == 0), "failed to test georadiusbymember with store option"); + + mems.clear(); + _redis.georadiusbymember(key, + "m1", + 100, + GeoUnit::KM, + 10, + true, + std::back_inserter(mems)); + REDIS_ASSERT(mems.size() == 3, "failed to test georadiusbymember with no option"); + + with_dist.clear(); + _redis.georadiusbymember(key, + "m1", + 100, + GeoUnit::KM, + 10, + true, + std::back_inserter(with_dist)); + REDIS_ASSERT(with_dist.size() == 3, "failed to test georadiusbymember with dist"); + + with_dist_coord.clear(); + _redis.georadiusbymember(key, + "m1", + 100, + GeoUnit::KM, + 10, + true, + std::back_inserter(with_dist_coord)); + REDIS_ASSERT(with_dist_coord.size() == 3, + "failed to test georadiusbymember with dist and coord"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hash_cmds_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hash_cmds_test.h new file mode 100644 index 000000000..60791c8b9 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hash_cmds_test.h @@ -0,0 +1,55 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class HashCmdTest { +public: + explicit HashCmdTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + void _test_hash(); + + void _test_hash_batch(); + + void _test_numeric(); + + void _test_hscan(); + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "hash_cmds_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hash_cmds_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hash_cmds_test.hpp new file mode 100644 index 000000000..13716c8d2 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hash_cmds_test.hpp @@ -0,0 +1,183 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP + +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void HashCmdTest::run() { + _test_hash(); + + _test_hash_batch(); + + _test_numeric(); + + _test_hscan(); +} + +template +void HashCmdTest::_test_hash() { + auto key = test_key("hash"); + + KeyDeleter deleter(_redis, key); + + auto f1 = std::string("f1"); + auto v1 = std::string("v1"); + auto f2 = std::string("f2"); + auto v2 = std::string("v2"); + auto f3 = std::string("f3"); + auto v3 = std::string("v3"); + + REDIS_ASSERT(_redis.hset(key, f1, v1), "failed to test hset"); + REDIS_ASSERT(!_redis.hset(key, f1, v2), "failed to test hset with exist field"); + + std::unordered_map m = {{"ff1", "vv1"}, {"ff2", "vv2"}}; + REDIS_ASSERT(_redis.hset(key, m.begin(), m.end()) == 2, "failed to test hset"); + REDIS_ASSERT(_redis.hset(key, {std::make_pair("ff1", "vv1"), + std::make_pair("ff2", "vv2")}) == 0, "failed to test hset"); + + auto res = _redis.hget(key, f1); + REDIS_ASSERT(res && *res == v2, "failed to test hget"); + + REDIS_ASSERT(_redis.hsetnx(key, f2, v1), "failed to test hsetnx"); + REDIS_ASSERT(!_redis.hsetnx(key, f2, v2), "failed to test hsetnx with exist field"); + + res = _redis.hget(key, f2); + REDIS_ASSERT(res && *res == v1, "failed to test hget"); + + REDIS_ASSERT(!_redis.hexists(key, f3), "failed to test hexists"); + REDIS_ASSERT(_redis.hset(key, std::make_pair(f3, v3)), "failed to test hset"); + REDIS_ASSERT(_redis.hexists(key, f3), "failed to test hexists"); + + REDIS_ASSERT(_redis.hlen(key) == 5, "failed to test hlen"); + REDIS_ASSERT(_redis.hstrlen(key, f1) == static_cast(v1.size()), + "failed to test hstrlen"); + + REDIS_ASSERT(_redis.hdel(key, f1) == 1, "failed to test hdel"); + REDIS_ASSERT(_redis.hdel(key, {f1, f2, f3}) == 2, "failed to test hdel range"); +} + +template +void HashCmdTest::_test_hash_batch() { + auto key = test_key("hash"); + + KeyDeleter deleter(_redis, key); + + auto f1 = std::string("f1"); + auto v1 = std::string("v1"); + auto f2 = std::string("f2"); + auto v2 = std::string("v2"); + auto f3 = std::string("f3"); + + _redis.hmset(key, {std::make_pair(f1, v1), + std::make_pair(f2, v2)}); + + std::vector fields; + _redis.hkeys(key, std::back_inserter(fields)); + REDIS_ASSERT(fields.size() == 2, "failed to test hkeys"); + + std::vector vals; + _redis.hvals(key, std::back_inserter(vals)); + REDIS_ASSERT(vals.size() == 2, "failed to test hvals"); + + std::unordered_map items; + _redis.hgetall(key, std::inserter(items, items.end())); + REDIS_ASSERT(items.size() == 2 && items[f1] == v1 && items[f2] == v2, + "failed to test hgetall"); + + std::vector> item_vec; + _redis.hgetall(key, std::back_inserter(item_vec)); + REDIS_ASSERT(item_vec.size() == 2, "failed to test hgetall"); + + std::vector res; + _redis.hmget(key, {f1, f2, f3}, std::back_inserter(res)); + REDIS_ASSERT(res.size() == 3 + && bool(res[0]) && *(res[0]) == v1 + && bool(res[1]) && *(res[1]) == v2 + && !res[2], + "failed to test hmget"); +} + +template +void HashCmdTest::_test_numeric() { + auto key = test_key("numeric"); + + KeyDeleter deleter(_redis, key); + + auto field = "field"; + + REDIS_ASSERT(_redis.hincrby(key, field, 1) == 1, "failed to test hincrby"); + REDIS_ASSERT(_redis.hincrby(key, field, -1) == 0, "failed to test hincrby"); + REDIS_ASSERT(_redis.hincrbyfloat(key, field, 1.5) == 1.5, "failed to test hincrbyfloat"); +} + +template +void HashCmdTest::_test_hscan() { + auto key = test_key("hscan"); + + KeyDeleter deleter(_redis, key); + + auto items = std::unordered_map{ + std::make_pair("f1", "v1"), + std::make_pair("f2", "v2"), + std::make_pair("f3", "v3"), + }; + + _redis.hmset(key, items.begin(), items.end()); + + std::unordered_map item_map; + auto cursor = 0; + while (true) { + cursor = _redis.hscan(key, cursor, "f*", 2, std::inserter(item_map, item_map.end())); + if (cursor == 0) { + break; + } + } + + REDIS_ASSERT(item_map == items, "failed to test hscan with pattern and count"); + + std::map item_vec; + cursor = 0; + while (true) { + cursor = _redis.hscan(key, cursor, std::inserter(item_vec, item_vec.begin())); + if (cursor == 0) { + break; + } + } + + REDIS_ASSERT(item_vec.size() == items.size(), "failed to test hscan"); + for (const auto &ele : item_vec) { + REDIS_ASSERT(items.find(ele.first) != items.end(), "failed to test hscan"); + } +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hyperloglog_cmds_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hyperloglog_cmds_test.h new file mode 100644 index 000000000..50e454b23 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hyperloglog_cmds_test.h @@ -0,0 +1,47 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class HyperloglogCmdTest { +public: + explicit HyperloglogCmdTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + RedisInstance &_redis; +}; + +} + +} + +} + +#include "hyperloglog_cmds_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hyperloglog_cmds_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hyperloglog_cmds_test.hpp new file mode 100644 index 000000000..09775255f --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/hyperloglog_cmds_test.hpp @@ -0,0 +1,67 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP + +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void HyperloglogCmdTest::run() { + auto k1 = test_key("k1"); + auto k2 = test_key("k2"); + auto k3 = test_key("k3"); + + KeyDeleter deleter(_redis, {k1, k2, k3}); + + _redis.pfadd(k1, "a"); + auto members1 = {"b", "c", "d", "e", "f", "g"}; + _redis.pfadd(k1, members1); + + auto cnt = _redis.pfcount(k1); + auto err = cnt * 1.0 / (1 + members1.size()); + REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfadd and pfcount"); + + auto members2 = {"a", "b", "c", "h", "i", "j", "k"}; + _redis.pfadd(k2, members2); + auto total = 1 + members1.size() + members2.size() - 3; + + cnt = _redis.pfcount({k1, k2}); + err = cnt * 1.0 / total; + REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfcount"); + + _redis.pfmerge(k3, {k1, k2}); + cnt = _redis.pfcount(k3); + err = cnt * 1.0 / total; + REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfcount"); + + _redis.pfmerge(k3, k1); + REDIS_ASSERT(cnt == _redis.pfcount(k3), "failed to test pfmerge"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/keys_cmds_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/keys_cmds_test.h new file mode 100644 index 000000000..54d25ca28 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/keys_cmds_test.h @@ -0,0 +1,55 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class KeysCmdTest { +public: + explicit KeysCmdTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + void _test_key(); + + void _test_randomkey(Redis &instance); + + void _test_ttl(); + + void _test_scan(Redis &instance); + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "keys_cmds_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/keys_cmds_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/keys_cmds_test.hpp new file mode 100644 index 000000000..de21f8d18 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/keys_cmds_test.hpp @@ -0,0 +1,166 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP + +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void KeysCmdTest::run() { + _test_key(); + + cluster_specializing_test(*this, &KeysCmdTest::_test_randomkey, _redis); + + _test_ttl(); + + cluster_specializing_test(*this, &KeysCmdTest::_test_scan, _redis); +} + +template +void KeysCmdTest::_test_key() { + auto key = test_key("key"); + auto dest = test_key("dest"); + auto new_key_name = test_key("new-key"); + auto not_exist_key = test_key("not-exist"); + + KeyDeleter deleter(_redis, {key, dest, new_key_name}); + + REDIS_ASSERT(_redis.exists(key) == 0, "failed to test exists"); + + auto val = std::string("val"); + _redis.set(key, val); + + REDIS_ASSERT(_redis.exists({key, not_exist_key}) == 1, "failed to test exists"); + + auto new_val = _redis.dump(key); + REDIS_ASSERT(bool(new_val), "failed to test dump"); + + _redis.restore(dest, *new_val, std::chrono::seconds(1000)); + + new_val = _redis.get(dest); + REDIS_ASSERT(bool(new_val) && *new_val == val, "failed to test dump and restore"); + + _redis.rename(dest, new_key_name); + + bool not_exist = false; + try { + _redis.rename(not_exist_key, new_key_name); + } catch (const Error &e) { + not_exist = true; + } + REDIS_ASSERT(not_exist, "failed to test rename with nonexistent key"); + + REDIS_ASSERT(_redis.renamenx(new_key_name, dest), "failed to test renamenx"); + + REDIS_ASSERT(_redis.touch(not_exist_key) == 0, "failed to test touch"); + REDIS_ASSERT(_redis.touch({key, dest, new_key_name}) == 2, "failed to test touch"); + + REDIS_ASSERT(_redis.type(key) == "string", "failed to test type"); + + REDIS_ASSERT(_redis.del({new_key_name, dest}) == 1, "failed to test del"); + REDIS_ASSERT(_redis.unlink({new_key_name, key}) == 1, "failed to test unlink"); +} + +template +void KeysCmdTest::_test_randomkey(Redis &instance) { + auto key = test_key("randomkey"); + + KeyDeleter deleter(instance, key); + + instance.set(key, "value"); + + auto rand_key = instance.randomkey(); + REDIS_ASSERT(bool(rand_key), "failed to test randomkey"); +} + +template +void KeysCmdTest::_test_ttl() { + using namespace std::chrono; + + auto key = test_key("ttl"); + + KeyDeleter deleter(_redis, key); + + _redis.set(key, "val", seconds(100)); + auto ttl = _redis.ttl(key); + REDIS_ASSERT(ttl > 0 && ttl <= 100, "failed to test ttl"); + + REDIS_ASSERT(_redis.persist(key), "failed to test persist"); + ttl = _redis.ttl(key); + REDIS_ASSERT(ttl == -1, "failed to test ttl"); + + REDIS_ASSERT(_redis.expire(key, seconds(100)), + "failed to test expire"); + + auto tp = time_point_cast(system_clock::now() + seconds(100)); + REDIS_ASSERT(_redis.expireat(key, tp), "failed to test expireat"); + ttl = _redis.ttl(key); + REDIS_ASSERT(ttl > 0, "failed to test expireat"); + + REDIS_ASSERT(_redis.pexpire(key, milliseconds(100000)), "failed to test expire"); + + auto pttl = _redis.pttl(key); + REDIS_ASSERT(pttl > 0 && pttl <= 100000, "failed to test pttl"); + + auto tp_milli = time_point_cast(system_clock::now() + milliseconds(100000)); + REDIS_ASSERT(_redis.pexpireat(key, tp_milli), "failed to test pexpireat"); + pttl = _redis.pttl(key); + REDIS_ASSERT(pttl > 0, "failed to test pexpireat"); +} + +template +void KeysCmdTest::_test_scan(Redis &instance) { + std::string key_pattern = "!@#$%^&()_+alseufoawhnlkszd"; + auto k1 = test_key(key_pattern + "k1"); + auto k2 = test_key(key_pattern + "k2"); + auto k3 = test_key(key_pattern + "k3"); + + auto keys = {k1, k2, k3}; + + KeyDeleter deleter(instance, keys); + + instance.set(k1, "v"); + instance.set(k2, "v"); + instance.set(k3, "v"); + + auto cursor = 0; + std::unordered_set res; + while (true) { + cursor = instance.scan(cursor, "*" + key_pattern + "*", 2, std::inserter(res, res.end())); + if (cursor == 0) { + break; + } + } + REDIS_ASSERT(res == std::unordered_set(keys), + "failed to test scan"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/list_cmds_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/list_cmds_test.h new file mode 100644 index 000000000..2092fe9e4 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/list_cmds_test.h @@ -0,0 +1,55 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class ListCmdTest { +public: + explicit ListCmdTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + void _test_lpoppush(); + + void _test_rpoppush(); + + void _test_list(); + + void _test_blocking(); + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "list_cmds_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/list_cmds_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/list_cmds_test.hpp new file mode 100644 index 000000000..fd26d8fde --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/list_cmds_test.hpp @@ -0,0 +1,154 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP + +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void ListCmdTest::run() { + _test_lpoppush(); + + _test_rpoppush(); + + _test_list(); + + _test_blocking(); +} + +template +void ListCmdTest::_test_lpoppush() { + auto key = test_key("lpoppush"); + + KeyDeleter deleter(_redis, key); + + auto item = _redis.lpop(key); + REDIS_ASSERT(!item, "failed to test lpop"); + + REDIS_ASSERT(_redis.lpushx(key, "1") == 0, "failed to test lpushx"); + REDIS_ASSERT(_redis.lpush(key, "1") == 1, "failed to test lpush"); + REDIS_ASSERT(_redis.lpushx(key, "2") == 2, "failed to test lpushx"); + REDIS_ASSERT(_redis.lpush(key, {"3", "4", "5"}) == 5, "failed to test lpush"); + + item = _redis.lpop(key); + REDIS_ASSERT(item && *item == "5", "failed to test lpop"); +} + +template +void ListCmdTest::_test_rpoppush() { + auto key = test_key("rpoppush"); + + KeyDeleter deleter(_redis, key); + + auto item = _redis.rpop(key); + REDIS_ASSERT(!item, "failed to test rpop"); + + REDIS_ASSERT(_redis.rpushx(key, "1") == 0, "failed to test rpushx"); + REDIS_ASSERT(_redis.rpush(key, "1") == 1, "failed to test rpush"); + REDIS_ASSERT(_redis.rpushx(key, "2") == 2, "failed to test rpushx"); + REDIS_ASSERT(_redis.rpush(key, {"3", "4", "5"}) == 5, "failed to test rpush"); + + item = _redis.rpop(key); + REDIS_ASSERT(item && *item == "5", "failed to test rpop"); +} + +template +void ListCmdTest::_test_list() { + auto key = test_key("list"); + + KeyDeleter deleter(_redis, key); + + auto item = _redis.lindex(key, 0); + REDIS_ASSERT(!item, "failed to test lindex"); + + _redis.lpush(key, {"1", "2", "3", "4", "5"}); + + REDIS_ASSERT(_redis.lrem(key, 0, "3") == 1, "failed to test lrem"); + + REDIS_ASSERT(_redis.linsert(key, InsertPosition::BEFORE, "2", "3") == 5, + "failed to test lindex"); + + REDIS_ASSERT(_redis.llen(key) == 5, "failed to test llen"); + + _redis.lset(key, 0, "6"); + item = _redis.lindex(key, 0); + REDIS_ASSERT(item && *item == "6", "failed to test lindex"); + + _redis.ltrim(key, 0, 2); + + std::vector res; + _redis.lrange(key, 0, -1, std::back_inserter(res)); + REDIS_ASSERT(res == std::vector({"6", "4", "3"}), "failed to test ltrim"); +} + +template +void ListCmdTest::_test_blocking() { + auto k1 = test_key("k1"); + auto k2 = test_key("k2"); + auto k3 = test_key("k3"); + + auto keys = {k1, k2, k3}; + + KeyDeleter deleter(_redis, keys); + + std::string val("value"); + _redis.lpush(k1, val); + + auto res = _redis.blpop(keys.begin(), keys.end()); + REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test blpop"); + + res = _redis.brpop(keys, std::chrono::seconds(1)); + REDIS_ASSERT(!res, "failed to test brpop with timeout"); + + _redis.lpush(k1, val); + res = _redis.blpop(k1); + REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test blpop"); + + res = _redis.blpop(k1, std::chrono::seconds(1)); + REDIS_ASSERT(!res, "failed to test blpop with timeout"); + + _redis.lpush(k1, val); + res = _redis.brpop(k1); + REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test brpop"); + + res = _redis.brpop(k1, std::chrono::seconds(1)); + REDIS_ASSERT(!res, "failed to test brpop with timeout"); + + auto str = _redis.brpoplpush(k2, k3, std::chrono::seconds(1)); + REDIS_ASSERT(!str, "failed to test brpoplpush with timeout"); + + _redis.lpush(k2, val); + str = _redis.brpoplpush(k2, k3); + REDIS_ASSERT(str && *str == val, "failed to test brpoplpush"); + + str = _redis.rpoplpush(k3, k2); + REDIS_ASSERT(str && *str == val, "failed to test rpoplpush"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pipeline_transaction_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pipeline_transaction_test.h new file mode 100644 index 000000000..9987894cc --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pipeline_transaction_test.h @@ -0,0 +1,61 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class PipelineTransactionTest { +public: + explicit PipelineTransactionTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + Pipeline _pipeline(const StringView &key, bool new_connection); + + Transaction _transaction(const StringView &key, bool piped, bool new_connection); + + void _test_pipeline(const StringView &key, Pipeline &pipe); + + void _test_pipeline_streams(const StringView &key, Pipeline &pipe); + + void _test_transaction(const StringView &key, Transaction &tx); + + void _test_watch(); + + void _test_error_handle(bool new_connection); + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "pipeline_transaction_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pipeline_transaction_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pipeline_transaction_test.hpp new file mode 100644 index 000000000..426161bb1 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pipeline_transaction_test.hpp @@ -0,0 +1,293 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP + +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void PipelineTransactionTest::run() { + { + auto key = test_key("pipeline"); + KeyDeleter deleter(_redis, key); + auto pipe = _pipeline(key, true); + _test_pipeline(key, pipe); + } + + { + auto key = test_key("pipeline"); + KeyDeleter deleter(_redis, key); + auto pipe = _pipeline(key, false); + _test_pipeline(key, pipe); + } + + { + auto key = test_key("pipeline"); + KeyDeleter deleter(_redis, key); + auto pipe = _pipeline(key, false); + _test_pipeline_streams(key, pipe); + } + + { + auto key = test_key("transaction"); + KeyDeleter deleter(_redis, key); + auto tx = _transaction(key, true, true); + _test_transaction(key, tx); + } + + { + auto key = test_key("transaction"); + KeyDeleter deleter(_redis, key); + auto tx = _transaction(key, false, true); + _test_transaction(key, tx); + } + + { + auto key = test_key("transaction"); + KeyDeleter deleter(_redis, key); + auto tx = _transaction(key, true, false); + _test_transaction(key, tx); + } + + { + auto key = test_key("transaction"); + KeyDeleter deleter(_redis, key); + auto tx = _transaction(key, false, false); + _test_transaction(key, tx); + } + + _test_watch(); + + _test_error_handle(true); + _test_error_handle(false); +} + +template +Pipeline PipelineTransactionTest::_pipeline(const StringView &, + bool new_connection) { + return _redis.pipeline(new_connection); +} + +template <> +inline Pipeline PipelineTransactionTest::_pipeline(const StringView &key, + bool new_connection) { + return _redis.pipeline(key, new_connection); +} + +template +Transaction PipelineTransactionTest::_transaction(const StringView &, + bool piped, bool new_connection) { + return _redis.transaction(piped, new_connection); +} + +template <> +inline Transaction PipelineTransactionTest::_transaction(const StringView &key, + bool piped, bool new_connection) { + return _redis.transaction(key, piped, new_connection); +} + +template +void PipelineTransactionTest::_test_pipeline(const StringView &key, + Pipeline &pipe) { + std::string val("value"); + auto replies = pipe.set(key, val) + .get(key) + .strlen(key) + .exec(); + + REDIS_ASSERT(replies.get(0), "failed to test pipeline with set operation"); + + auto new_val = replies.get(1); + std::size_t len = replies.get(2); + REDIS_ASSERT(bool(new_val) && *new_val == val && len == val.size(), + "failed to test pipeline with string operations"); + + REDIS_ASSERT(reply::parse(replies.get(0)), "failed to test pipeline with set operation"); + + new_val = reply::parse(replies.get(1)); + len = reply::parse(replies.get(2)); + REDIS_ASSERT(bool(new_val) && *new_val == val && len == val.size(), + "failed to test pipeline with string operations"); +} + +template +void PipelineTransactionTest::_test_pipeline_streams(const StringView &key, + Pipeline &pipe) { + + const std::vector ids = {"1565427842-0", "1565427842-1"}; + std::vector> attrs = { + {"f1", "v1"}, + {"f2", "v2"} + }; + for (auto id : ids) { + pipe.xadd(key, id, attrs.begin(), attrs.end()); + } + for (auto id : ids) { + pipe.xrange(key, "-", "+", 1); + pipe.xrevrange(key, "+", "-", 1); + } + auto replies = pipe.exec(); + int replyCnt = 0; + using Item = std::pair>; + std::vector items; + for (auto id : ids) { + auto reply = replies.get(replyCnt++); + std::string retId = reply::parse(reply); + REDIS_ASSERT(retId == id, "failed to test pipeline_streams with xadd"); + } + for (auto id : ids) { + auto replyXrange = replies.get(replyCnt++); + std::vector items = reply::parse>(replyXrange); + auto replyXrevrange = replies.get(replyCnt++); + std::vector itemsRev = reply::parse>(replyXrevrange); + REDIS_ASSERT( + items.size() == 1 && + items.at(0).first == ids.at(0) && + itemsRev.at(0).first == ids.at(ids.size()-1), + "failed to test pipeline_streams with count"); + } +} + +template +void PipelineTransactionTest::_test_transaction(const StringView &key, + Transaction &tx) { + std::unordered_map m = { + std::make_pair("f1", "v1"), + std::make_pair("f2", "v2"), + std::make_pair("f3", "v3") + }; + auto replies = tx.hmset(key, m.begin(), m.end()) + .hgetall(key) + .hdel(key, "f1") + .exec(); + + replies.get(0); + + decltype(m) mm; + replies.get(1, std::inserter(mm, mm.end())); + REDIS_ASSERT(mm == m, "failed to test transaction"); + + REDIS_ASSERT(replies.get(2) == 1, "failed to test transaction"); + + tx.set(key, "value") + .get(key) + .incr(key); + + tx.discard(); + + replies = tx.del(key) + .set(key, "value") + .exec(); + + REDIS_ASSERT(replies.get(0) == 1, "failed to test transaction"); + + REDIS_ASSERT(replies.get(1), "failed to test transaction"); +} + +template +void PipelineTransactionTest::_test_watch() { + auto key = test_key("watch"); + + KeyDeleter deleter(_redis, key); + + { + auto tx = _transaction(key, false, true); + + auto redis = tx.redis(); + + redis.watch(key); + + auto replies = tx.set(key, "1").get(key).exec(); + + REDIS_ASSERT(replies.size() == 2 + && replies.template get(0) == true, "failed to test watch"); + + auto val = replies.template get(1); + + REDIS_ASSERT(val && *val == "1", "failed to test watch"); + } + + try { + auto tx = _transaction(key, false, true); + + auto redis = tx.redis(); + + redis.watch(key); + + // Key has been modified by other client. + _redis.set(key, "val"); + + // Transaction should fail, and throw WatchError + tx.set(key, "1").exec(); + + REDIS_ASSERT(false, "failed to test watch"); + } catch (const sw::redis::WatchError &err) { + // Catch the error. + } +} + +template +void PipelineTransactionTest::_test_error_handle(bool new_connection) { + auto key = test_key("error_handle"); + + KeyDeleter deleter(_redis, key); + + try { + auto pipe = _pipeline(key, new_connection); + + // This will fail + pipe.set(key, "val").hget(key, "field").exec(); + } catch (const sw::redis::Error &err) { + // Ignore the error. + } + + auto pipe = _pipeline(key, new_connection); + std::string val("val"); + auto replies = pipe.set(key, val).get(key).exec(); + REDIS_ASSERT(replies.size() == 2, "failed to test pipeline's error handling"); + auto value = replies.template get(1); + REDIS_ASSERT(value && *value == val, "failed to test pipeline's error handling"); + + { + auto pipe = _pipeline(key, new_connection); + pipe.set(key, "val"); + + // Forget to call exec() or discard() + } + + pipe = _pipeline(key, new_connection); + val = "new value"; + replies = pipe.set(key, val).get(key).exec(); + REDIS_ASSERT(replies.size() == 2, "failed to test pipeline's error handling"); + value = replies.template get(1); + REDIS_ASSERT(value && *value == val, "failed to test pipeline's error handling"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pubsub_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pubsub_test.h new file mode 100644 index 000000000..f816eaecc --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pubsub_test.h @@ -0,0 +1,53 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class PubSubTest { +public: + explicit PubSubTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + void _test_sub_channel(); + + void _test_sub_pattern(); + + void _test_unsubscribe(); + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "pubsub_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pubsub_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pubsub_test.hpp new file mode 100644 index 000000000..4530c0788 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/pubsub_test.hpp @@ -0,0 +1,244 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_HPP + +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void PubSubTest::run() { + _test_sub_channel(); + + _test_sub_pattern(); + + _test_unsubscribe(); +} + +template +void PubSubTest::_test_sub_channel() { + auto sub = _redis.subscriber(); + + auto msgs = {"msg1", "msg2"}; + auto channel1 = test_key("c1"); + sub.on_message([&msgs, &channel1](std::string channel, std::string msg) { + static std::size_t idx = 0; + REDIS_ASSERT(channel == channel1 && msg == *(msgs.begin() + idx), + "failed to test subscribe"); + ++idx; + }); + + sub.subscribe(channel1); + + // Consume the SUBSCRIBE message. + sub.consume(); + + for (const auto &msg : msgs) { + _redis.publish(channel1, msg); + sub.consume(); + } + + sub.unsubscribe(channel1); + + // Consume the UNSUBSCRIBE message. + sub.consume(); + + auto channel2 = test_key("c2"); + auto channel3 = test_key("c3"); + auto channel4 = test_key("c4"); + std::unordered_set channels; + sub.on_meta([&channels](Subscriber::MsgType type, + OptionalString channel, + long long num) { + REDIS_ASSERT(bool(channel), "failed to test subscribe"); + + if (type == Subscriber::MsgType::SUBSCRIBE) { + auto iter = channels.find(*channel); + REDIS_ASSERT(iter == channels.end(), "failed to test subscribe"); + channels.insert(*channel); + REDIS_ASSERT(static_cast(num) == channels.size(), + "failed to test subscribe"); + } else if (type == Subscriber::MsgType::UNSUBSCRIBE) { + auto iter = channels.find(*channel); + REDIS_ASSERT(iter != channels.end(), "failed to test subscribe"); + channels.erase(*channel); + REDIS_ASSERT(static_cast(num) == channels.size(), + "failed to test subscribe"); + } else { + REDIS_ASSERT(false, "Unknown message type"); + } + }); + + std::unordered_map messages = { + {channel2, "msg2"}, + {channel3, "msg3"}, + {channel4, "msg4"}, + }; + sub.on_message([&messages](std::string channel, std::string msg) { + REDIS_ASSERT(messages.find(channel) != messages.end(), + "failed to test subscribe"); + REDIS_ASSERT(messages[channel] == msg, "failed to test subscribe"); + }); + + sub.subscribe({channel2, channel3, channel4}); + + for (std::size_t idx = 0; idx != channels.size(); ++idx) { + sub.consume(); + } + + for (const auto &ele : messages) { + _redis.publish(ele.first, ele.second); + sub.consume(); + } + + auto tmp = {channel2, channel3, channel4}; + sub.unsubscribe(tmp); + + for (std::size_t idx = 0; idx != tmp.size(); ++idx) { + sub.consume(); + } +} + +template +void PubSubTest::_test_sub_pattern() { + auto sub = _redis.subscriber(); + + auto msgs = {"msg1", "msg2"}; + auto pattern1 = test_key("pattern*"); + std::string channel1 = test_key("pattern1"); + sub.on_pmessage([&msgs, &pattern1, &channel1](std::string pattern, + std::string channel, + std::string msg) { + static std::size_t idx = 0; + REDIS_ASSERT(pattern == pattern1 + && channel == channel1 + && msg == *(msgs.begin() + idx), + "failed to test psubscribe"); + ++idx; + }); + + sub.psubscribe(pattern1); + + // Consume the PSUBSCRIBE message. + sub.consume(); + + for (const auto &msg : msgs) { + _redis.publish(channel1, msg); + sub.consume(); + } + + sub.punsubscribe(pattern1); + + // Consume the PUNSUBSCRIBE message. + sub.consume(); + + auto channel2 = test_key("pattern22"); + auto channel3 = test_key("pattern33"); + auto channel4 = test_key("pattern44"); + std::unordered_set channels; + sub.on_meta([&channels](Subscriber::MsgType type, + OptionalString channel, + long long num) { + REDIS_ASSERT(bool(channel), "failed to test psubscribe"); + + if (type == Subscriber::MsgType::PSUBSCRIBE) { + auto iter = channels.find(*channel); + REDIS_ASSERT(iter == channels.end(), "failed to test psubscribe"); + channels.insert(*channel); + REDIS_ASSERT(static_cast(num) == channels.size(), + "failed to test psubscribe"); + } else if (type == Subscriber::MsgType::PUNSUBSCRIBE) { + auto iter = channels.find(*channel); + REDIS_ASSERT(iter != channels.end(), "failed to test psubscribe"); + channels.erase(*channel); + REDIS_ASSERT(static_cast(num) == channels.size(), + "failed to test psubscribe"); + } else { + REDIS_ASSERT(false, "Unknown message type"); + } + }); + + auto pattern2 = test_key("pattern2*"); + auto pattern3 = test_key("pattern3*"); + auto pattern4 = test_key("pattern4*"); + std::unordered_set patterns = {pattern2, pattern3, pattern4}; + + std::unordered_map messages = { + {channel2, "msg2"}, + {channel3, "msg3"}, + {channel4, "msg4"}, + }; + sub.on_pmessage([&patterns, &messages](std::string pattern, + std::string channel, + std::string msg) { + REDIS_ASSERT(patterns.find(pattern) != patterns.end(), + "failed to test psubscribe"); + REDIS_ASSERT(messages[channel] == msg, "failed to test psubscribe"); + }); + + sub.psubscribe({pattern2, pattern3, pattern4}); + + for (std::size_t idx = 0; idx != channels.size(); ++idx) { + sub.consume(); + } + + for (const auto &ele : messages) { + _redis.publish(ele.first, ele.second); + sub.consume(); + } + + auto tmp = {pattern2, pattern3, pattern4}; + sub.punsubscribe(tmp); + + for (std::size_t idx = 0; idx != tmp.size(); ++idx) { + sub.consume(); + } +} + +template +void PubSubTest::_test_unsubscribe() { + auto sub = _redis.subscriber(); + + sub.on_meta([](Subscriber::MsgType type, + OptionalString channel, + long long num) { + REDIS_ASSERT(type == Subscriber::MsgType::UNSUBSCRIBE, + "failed to test unsub"); + + REDIS_ASSERT(!channel, "failed to test unsub"); + + REDIS_ASSERT(num == 0, "failed to test unsub"); + }); + + sub.unsubscribe(); + sub.consume(); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/sanity_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/sanity_test.h new file mode 100644 index 000000000..eabbdbd1e --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/sanity_test.h @@ -0,0 +1,80 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class SanityTest { +public: + SanityTest(const ConnectionOptions &opts, RedisInstance &instance) + : _opts(opts), _redis(instance) {} + + void run(); + +private: + void _test_uri(); + + void _test_uri_ctor(); + + std::string _build_uri(const ConnectionOptions &opts) const; + + void _ping(Redis &instance); + + void _test_move_ctor(); + + void _test_cmdargs(); + + void _test_generic_command(); + + void _test_hash_tag(); + + void _test_hash_tag(std::initializer_list keys); + + std::string _test_key(const std::string &key); + + void _test_ping(Redis &instance); + + void _test_pipeline(const StringView &key, Pipeline &pipeline); + + void _test_transaction(const StringView &key, Transaction &transaction); + + Pipeline _pipeline(const StringView &key); + + Transaction _transaction(const StringView &key); + + ConnectionOptions _opts; + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "sanity_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/sanity_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/sanity_test.hpp new file mode 100644 index 000000000..482fb17de --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/sanity_test.hpp @@ -0,0 +1,481 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_HPP + +#include "utils.h" +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +void SanityTest::run() { + _test_uri(); + + _test_uri_ctor(); + + _test_move_ctor(); + + cluster_specializing_test(*this, &SanityTest::_test_ping, _redis); + + auto pipe_key = test_key("pipeline"); + auto tx_key = test_key("transaction"); + + KeyDeleter deleter(_redis, {pipe_key, tx_key}); + + auto pipeline = _pipeline(pipe_key); + _test_pipeline(pipe_key, pipeline); + + auto transaction = _transaction(tx_key); + _test_transaction(tx_key, transaction); + + _test_cmdargs(); + + _test_generic_command(); +} + +template +void SanityTest::_test_uri_ctor() { + std::string uri; + switch (_opts.type) { + case sw::redis::ConnectionType::TCP: + uri = _build_uri(_opts); + break; + + case sw::redis::ConnectionType::UNIX: + REDIS_ASSERT(false, "NO test for UNIX Domain Socket"); + break; + + default: + REDIS_ASSERT(false, "Unknown connection type"); + } + + auto instance = RedisInstance(uri); + + cluster_specializing_test(*this, &SanityTest::_ping, instance); +} + +template +std::string SanityTest::_build_uri(const ConnectionOptions &opts) const { + auto scheme = "tcp://"; + auto uri = opts.host + ":" + std::to_string(opts.port) + "/" + std::to_string(opts.db); + + std::string auth; + if (opts.user != "default") { + auth += opts.user + ":"; + } + + if (!opts.password.empty()) { + auth += opts.password; + } + + if (!auth.empty()) { + auth += "@"; + } + + return scheme + auth + uri; +} + +template +void SanityTest::_ping(Redis &instance) { + REDIS_ASSERT(instance.ping() == "PONG", "Failed to test constructing Redis with uri"); +} + +template +void SanityTest::_test_move_ctor() { + auto test_move_ctor = std::move(_redis); + + _redis = std::move(test_move_ctor); +} + +template +void SanityTest::_test_cmdargs() { + auto lpush_num = [](Connection &connection, const StringView &key, long long num) { + connection.send("LPUSH %b %lld", + key.data(), key.size(), + num); + }; + + auto lpush_nums = [](Connection &connection, + const StringView &key, + const std::vector &nums) { + CmdArgs args; + args.append("LPUSH").append(key); + for (auto num : nums) { + args.append(std::to_string(num)); + } + + connection.send(args); + }; + + auto key = test_key("lpush_num"); + + KeyDeleter deleter(_redis, key); + + auto reply = _redis.command(lpush_num, key, 1); + REDIS_ASSERT(reply::parse(*reply) == 1, "failed to test cmdargs"); + + std::vector nums = {2, 3, 4, 5}; + reply = _redis.command(lpush_nums, key, nums); + REDIS_ASSERT(reply::parse(*reply) == 5, "failed to test cmdargs"); + + std::vector res; + _redis.lrange(key, 0, -1, std::back_inserter(res)); + REDIS_ASSERT((res == std::vector{"5", "4", "3", "2", "1"}), + "failed to test cmdargs"); +} + +template +void SanityTest::_test_generic_command() { + auto key = test_key("key"); + auto not_exist_key = test_key("not_exist_key"); + auto k1 = test_key("k1"); + auto k2 = test_key("k2"); + auto key_var = test_key("key_var"); + + KeyDeleter deleter(_redis, {key, not_exist_key, k1, k2, key_var}); + + std::string cmd("set"); + _redis.command(cmd, key, 123); + auto reply = _redis.command("get", key); + auto val = reply::parse(*reply); + REDIS_ASSERT(val && *val == "123", "failed to test generic command"); + + val = _redis.template command("get", key); + REDIS_ASSERT(val && *val == "123", "failed to test generic command"); + + std::vector res; + _redis.command("mget", key, not_exist_key, std::back_inserter(res)); + REDIS_ASSERT(res.size() == 2 && res[0] && *res[0] == "123" && !res[1], + "failed to test generic command"); + + reply = _redis.command("incr", key); + REDIS_ASSERT(reply::parse(*reply) == 124, "failed to test generic command"); + + _redis.command("mset", k1.c_str(), "v", k2.c_str(), "v"); + reply = _redis.command("mget", k1, k2); + res.clear(); + reply::to_array(*reply, std::back_inserter(res)); + REDIS_ASSERT(res.size() == 2 && res[0] && *(res[0]) == "v" && res[1] && *(res[1]) == "v", + "failed to test generic command"); + + res = _redis.template command>("mget", k1, k2); + REDIS_ASSERT(res.size() == 2 && res[0] && *(res[0]) == "v" && res[1] && *(res[1]) == "v", + "failed to test generic command"); + + res.clear(); + _redis.command("mget", k1, k2, std::back_inserter(res)); + REDIS_ASSERT(res.size() == 2 && res[0] && *(res[0]) == "v" && res[1] && *(res[1]) == "v", + "failed to test generic command"); + + auto set_cmd_str = {"set", key.c_str(), "new_value"}; + _redis.command(set_cmd_str.begin(), set_cmd_str.end()); + + auto get_cmd_str = {"get", key.c_str()}; + reply = _redis.command(get_cmd_str.begin(), get_cmd_str.end()); + val = reply::parse(*reply); + REDIS_ASSERT(val && *val == "new_value", "failed to test generic command"); + + val = _redis.template command(get_cmd_str.begin(), get_cmd_str.end()); + REDIS_ASSERT(val && *val == "new_value", "failed to test generic command"); + + auto mget_cmd_str = {"mget", key.c_str(), not_exist_key.c_str()}; + res.clear(); + _redis.command(mget_cmd_str.begin(), mget_cmd_str.end(), std::back_inserter(res)); + REDIS_ASSERT(res.size() == 2 && res[0] && *res[0] == "new_value" && !res[1], + "failed to test generic command"); + +#ifdef REDIS_PLUS_PLUS_HAS_VARIANT + + _redis.hmset(key_var, {std::make_pair("a", "abc"), std::make_pair("b", "1.2")}); + std::unordered_map> var_result; + _redis.hgetall(key_var, std::inserter(var_result, var_result.begin())); + REDIS_ASSERT(var_result.size() == 2 + && std::get(var_result["a"]) == "abc" + && (std::get(var_result["b"]) - 1.2) < 0.01, + "failed to test generic command with variant reply"); + + std::unordered_map> var_with_monostate; + _redis.hgetall(key_var, std::inserter(var_with_monostate, var_with_monostate.begin())); + REDIS_ASSERT(var_with_monostate.size() == 2 + && std::holds_alternative(var_with_monostate["a"]) + && std::holds_alternative(var_with_monostate["b"]), + "failed to test generic command with variant reply"); + +#endif +} + +template +void SanityTest::_test_hash_tag() { + _test_hash_tag({_test_key("{tag}postfix1"), + _test_key("{tag}postfix2"), + _test_key("{tag}postfix3")}); + + _test_hash_tag({_test_key("prefix1{tag}postfix1"), + _test_key("prefix2{tag}postfix2"), + _test_key("prefix3{tag}postfix3")}); + + _test_hash_tag({_test_key("prefix1{tag}"), + _test_key("prefix2{tag}"), + _test_key("prefix3{tag}")}); + + _test_hash_tag({_test_key("prefix{}postfix"), + _test_key("prefix{}postfix"), + _test_key("prefix{}postfix")}); + + _test_hash_tag({_test_key("prefix1{tag}post}fix1"), + _test_key("prefix2{tag}pos}tfix2"), + _test_key("prefix3{tag}postfi}x3")}); + + _test_hash_tag({_test_key("prefix1{t{ag}postfix1"), + _test_key("prefix2{t{ag}postfix2"), + _test_key("prefix3{t{ag}postfix3")}); + + _test_hash_tag({_test_key("prefix1{t{ag}postfi}x1"), + _test_key("prefix2{t{ag}post}fix2"), + _test_key("prefix3{t{ag}po}stfix3")}); +} + +template +void SanityTest::_test_hash_tag(std::initializer_list keys) { + KeyDeleter deleter(_redis, keys.begin(), keys.end()); + + std::string value = "value"; + std::vector> kvs; + for (const auto &key : keys) { + kvs.emplace_back(key, value); + } + + _redis.mset(kvs.begin(), kvs.end()); + + std::vector res; + res.reserve(keys.size()); + _redis.mget(keys.begin(), keys.end(), std::back_inserter(res)); + + REDIS_ASSERT(res.size() == keys.size(), "failed to test hash tag"); + + for (const auto &ele : res) { + REDIS_ASSERT(ele && *ele == value, "failed to test hash tag"); + } +} + +template +std::string SanityTest::_test_key(const std::string &key) { + REDIS_ASSERT(key.size() > 1, "failed to generate key"); + + // Ensure that key prefix has NO hash tag. Also see the implementation of test_key. + return key.substr(1); +} + +template +void SanityTest::_test_ping(Redis &instance) { + auto reply = instance.command("ping"); + REDIS_ASSERT(reply && reply::parse(*reply) == "PONG", + "failed to test generic command"); + + auto pong = instance.command("ping"); + REDIS_ASSERT(pong == "PONG", "failed to test generic command"); +} + +template +void SanityTest::_test_pipeline(const StringView &key, Pipeline &pipeline) { + auto pipe_replies = pipeline.command("set", key, "value").command("get", key).exec(); + auto val = pipe_replies.get(1); + REDIS_ASSERT(val && *val == "value", "failed to test generic command"); +} + +template +void SanityTest::_test_transaction(const StringView &key, Transaction &transaction) { + auto tx_replies = transaction.command("set", key, 456).command("incr", key).exec(); + REDIS_ASSERT(tx_replies.get(1) == 457, "failed to test generic command"); +} + +template +Pipeline SanityTest::_pipeline(const StringView &) { + return _redis.pipeline(); +} + +template <> +inline Pipeline SanityTest::_pipeline(const StringView &key) { + return _redis.pipeline(key); +} + +template +Transaction SanityTest::_transaction(const StringView &) { + return _redis.transaction(); +} + +template <> +inline Transaction SanityTest::_transaction(const StringView &key) { + return _redis.transaction(key); +} + +template +void SanityTest::_test_uri() { + auto opts = ConnectionOptions("tcp://user:pass@127.0.0.1:7000/1"); + REDIS_ASSERT(opts.user == "user" && opts.password == "pass" + && opts.host == "127.0.0.1" && opts.port == 7000 + && opts.db == 1 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://user:pass@127.0.0.1:7000"); + REDIS_ASSERT(opts.user == "user" && opts.password == "pass" + && opts.host == "127.0.0.1" && opts.port == 7000 + && opts.db == 0 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://user:pass@127.0.0.1/1"); + REDIS_ASSERT(opts.user == "user" && opts.password == "pass" + && opts.host == "127.0.0.1" && opts.port == 6379 + && opts.db == 1 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://user:pass@127.0.0.1"); + REDIS_ASSERT(opts.user == "user" && opts.password == "pass" + && opts.host == "127.0.0.1" && opts.port == 6379 + && opts.db == 0 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://pass@127.0.0.1"); + REDIS_ASSERT(opts.user == "default" && opts.password == "pass" + && opts.host == "127.0.0.1" && opts.port == 6379 + && opts.db == 0 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://pass@127.0.0.1/1"); + REDIS_ASSERT(opts.user == "default" && opts.password == "pass" + && opts.host == "127.0.0.1" && opts.port == 6379 + && opts.db == 1 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://pass@127.0.0.1:7000/1"); + REDIS_ASSERT(opts.user == "default" && opts.password == "pass" + && opts.host == "127.0.0.1" && opts.port == 7000 + && opts.db == 1 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://user:@127.0.0.1:7000/1"); + REDIS_ASSERT(opts.user == "user" && opts.password == "" + && opts.host == "127.0.0.1" && opts.port == 7000 + && opts.db == 1 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://user:@127.0.0.1:7000"); + REDIS_ASSERT(opts.user == "user" && opts.password == "" + && opts.host == "127.0.0.1" && opts.port == 7000 + && opts.db == 0 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://user:@127.0.0.1"); + REDIS_ASSERT(opts.user == "user" && opts.password == "" + && opts.host == "127.0.0.1" && opts.port == 6379 + && opts.db == 0 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://127.0.0.1"); + REDIS_ASSERT(opts.user == "default" && opts.password == "" + && opts.host == "127.0.0.1" && opts.port == 6379 + && opts.db == 0 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://127.0.0.1:7000"); + REDIS_ASSERT(opts.user == "default" && opts.password == "" + && opts.host == "127.0.0.1" && opts.port == 7000 + && opts.db == 0 && opts.type == ConnectionType::TCP, + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://127.0.0.1:7000/1?keep_alive=true&connect_timeout=300ms&socket_timeout=1s"); + REDIS_ASSERT(opts.user == "default" && opts.password == "" + && opts.host == "127.0.0.1" && opts.port == 7000 + && opts.db == 1 && opts.type == ConnectionType::TCP + && opts.keep_alive && opts.connect_timeout == std::chrono::milliseconds(300) + && opts.socket_timeout == std::chrono::seconds(1), + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://pass@127.0.0.1:7000?connect_timeout=300ms&socket_timeout=1s"); + REDIS_ASSERT(opts.user == "default" && opts.password == "pass" + && opts.host == "127.0.0.1" && opts.port == 7000 + && opts.db == 0 && opts.type == ConnectionType::TCP + && opts.connect_timeout == std::chrono::milliseconds(300) + && opts.socket_timeout == std::chrono::seconds(1), + "failed to test uri construction"); + + opts = ConnectionOptions("tcp://user:pass@127.0.0.1?connect_timeout=300ms&socket_timeout=1s&keep_alive=false"); + REDIS_ASSERT(opts.user == "user" && opts.password == "pass" + && opts.host == "127.0.0.1" && opts.port == 6379 + && opts.db == 0 && opts.type == ConnectionType::TCP + && opts.connect_timeout == std::chrono::milliseconds(300) + && opts.socket_timeout == std::chrono::seconds(1) && !opts.keep_alive, + "failed to test uri construction"); + + opts = ConnectionOptions("unix://user:pass@path/to/unix/domain.sock/1"); + REDIS_ASSERT(opts.user == "user" && opts.password == "pass" + && opts.path == "path/to/unix/domain.sock" + && opts.db == 1 && opts.type == ConnectionType::UNIX, + "failed to test uri construction"); + + opts = ConnectionOptions("unix://user:pass@path/to/unix/domain.sock"); + REDIS_ASSERT(opts.user == "user" && opts.password == "pass" + && opts.path == "path/to/unix/domain.sock" + && opts.db == 0 && opts.type == ConnectionType::UNIX, + "failed to test uri construction"); + + opts = ConnectionOptions("unix://pass@path/to/unix/domain.sock"); + REDIS_ASSERT(opts.user == "default" && opts.password == "pass" + && opts.path == "path/to/unix/domain.sock" + && opts.db == 0 && opts.type == ConnectionType::UNIX, + "failed to test uri construction"); + + opts = ConnectionOptions("unix://user:@path/to/unix/domain.sock"); + REDIS_ASSERT(opts.user == "user" && opts.password == "" + && opts.path == "path/to/unix/domain.sock" + && opts.db == 0 && opts.type == ConnectionType::UNIX, + "failed to test uri construction"); + + opts = ConnectionOptions("unix://path/to/unix/domain.sock"); + REDIS_ASSERT(opts.user == "default" && opts.password == "" + && opts.path == "path/to/unix/domain.sock" + && opts.db == 0 && opts.type == ConnectionType::UNIX, + "failed to test uri construction"); + + opts = ConnectionOptions("unix://path/to/unix/domain.sock?keep_alive=false&socket_timeout=100ms"); + REDIS_ASSERT(opts.user == "default" && opts.password == "" + && opts.path == "path/to/unix/domain.sock" + && opts.db == 0 && opts.type == ConnectionType::UNIX + && !opts.keep_alive && opts.socket_timeout == std::chrono::milliseconds(100), + "failed to test uri construction"); + + opts = ConnectionOptions("unix://user:pass@path/to/unix/domain.sock/1?connect_timeout=1s&keep_alive=true&socket_timeout=100ms"); + REDIS_ASSERT(opts.user == "user" && opts.password == "pass" + && opts.path == "path/to/unix/domain.sock" + && opts.db == 1 && opts.type == ConnectionType::UNIX + && opts.connect_timeout == std::chrono::seconds(1) && opts.keep_alive + && opts.socket_timeout == std::chrono::milliseconds(100), + "failed to test uri construction"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/script_cmds_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/script_cmds_test.h new file mode 100644 index 000000000..f7adb11b6 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/script_cmds_test.h @@ -0,0 +1,49 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class ScriptCmdTest { +public: + explicit ScriptCmdTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + void _run(Redis &instance); + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "script_cmds_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/script_cmds_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/script_cmds_test.hpp new file mode 100644 index 000000000..86a0e7dfd --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/script_cmds_test.hpp @@ -0,0 +1,126 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_HPP + +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void ScriptCmdTest::run() { + cluster_specializing_test(*this, + &ScriptCmdTest::_run, + _redis); +} + +template +void ScriptCmdTest::_run(Redis &instance) { + auto key1 = test_key("k1"); + auto key2 = test_key("k2"); + + KeyDeleter deleter(instance, {key1, key2}); + + std::string script = "redis.call('set', KEYS[1], 1);" + "redis.call('set', KEYS[2], 2);" + "local first = redis.call('get', KEYS[1]);" + "local second = redis.call('get', KEYS[2]);" + "return first + second"; + + std::initializer_list keys = {key1, key2}; + std::initializer_list empty_list = {}; + + auto num = instance.eval(script, keys, empty_list); + REDIS_ASSERT(num == 3, "failed to test scripting for cluster"); + + num = instance.eval(script, keys.begin(), keys.end(), + empty_list.begin(), empty_list.end()); + REDIS_ASSERT(num == 3, "failed to test scripting for cluster"); + + script = "return 1"; + num = instance.eval(script, empty_list, empty_list); + REDIS_ASSERT(num == 1, "failed to test eval"); + + num = instance.eval(script, empty_list.begin(), empty_list.end(), + empty_list.begin(), empty_list.end()); + REDIS_ASSERT(num == 1, "failed to test eval"); + + auto script_with_args = "return {ARGV[1] + 1, ARGV[2] + 2, ARGV[3] + 3}"; + std::initializer_list args = {"1", "2", "3"}; + std::vector res; + instance.eval(script_with_args, + empty_list, + args, + std::back_inserter(res)); + REDIS_ASSERT(res == std::vector({2, 4, 6}), + "failed to test eval with array reply"); + + res.clear(); + instance.eval(script_with_args, + empty_list.begin(), empty_list.end(), + args.begin(), args.end(), + std::back_inserter(res)); + REDIS_ASSERT(res == std::vector({2, 4, 6}), + "failed to test eval with array reply"); + + auto sha1 = instance.script_load(script); + num = instance.evalsha(sha1, {}, {}); + REDIS_ASSERT(num == 1, "failed to test evalsha"); + + num = instance.evalsha(sha1, empty_list.begin(), empty_list.end(), + empty_list.begin(), empty_list.end()); + REDIS_ASSERT(num == 1, "failed to test evalsha"); + + auto sha2 = instance.script_load(script_with_args); + res.clear(); + instance.evalsha(sha2, + empty_list, + args, + std::back_inserter(res)); + REDIS_ASSERT(res == std::vector({2, 4, 6}), + "failed to test evalsha with array reply"); + + res.clear(); + instance.evalsha(sha2, + empty_list.begin(), empty_list.end(), + args.begin(), args.end(), + std::back_inserter(res)); + REDIS_ASSERT(res == std::vector({2, 4, 6}), + "failed to test evalsha with array reply"); + + std::list exist_res; + instance.script_exists({sha1, sha2, std::string("not exist")}, std::back_inserter(exist_res)); + REDIS_ASSERT(exist_res == std::list({true, true, false}), + "failed to test script exists"); + + REDIS_ASSERT(instance.script_exists(sha1), "failed to test script exists"); + REDIS_ASSERT(!instance.script_exists("not exist"), "failed to test script exists"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/set_cmds_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/set_cmds_test.h new file mode 100644 index 000000000..c5320d793 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/set_cmds_test.h @@ -0,0 +1,53 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class SetCmdTest { +public: + explicit SetCmdTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + void _test_set(); + + void _test_multi_set(); + + void _test_sscan(); + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "set_cmds_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/set_cmds_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/set_cmds_test.hpp new file mode 100644 index 000000000..1a4c24bfd --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/set_cmds_test.hpp @@ -0,0 +1,184 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_HPP + +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void SetCmdTest::run() { + _test_set(); + + _test_multi_set(); + + _test_sscan(); +} + +template +void SetCmdTest::_test_set() { + auto key = test_key("set"); + + KeyDeleter deleter(_redis, key); + + std::string m1("m1"); + std::string m2("m2"); + std::string m3("m3"); + + REDIS_ASSERT(_redis.sadd(key, m1) == 1, "failed to test sadd"); + + auto members = {m1, m2, m3}; + REDIS_ASSERT(_redis.sadd(key, members) == 2, "failed to test sadd with multiple members"); + + REDIS_ASSERT(_redis.scard(key) == 3, "failed to test scard"); + + REDIS_ASSERT(_redis.sismember(key, m1), "failed to test sismember"); + + std::unordered_set res; + _redis.smembers(key, std::inserter(res, res.end())); + REDIS_ASSERT(res.find(m1) != res.end() + && res.find(m2) != res.end() + && res.find(m3) != res.end(), + "failed to test smembers"); + + auto ele = _redis.srandmember(key); + REDIS_ASSERT(bool(ele) && res.find(*ele) != res.end(), "failed to test srandmember"); + + std::vector rand_members; + _redis.srandmember(key, 2, std::back_inserter(rand_members)); + REDIS_ASSERT(rand_members.size() == 2, "failed to test srandmember"); + + ele = _redis.spop(key); + REDIS_ASSERT(bool(ele) && res.find(*ele) != res.end(), "failed to test spop"); + + rand_members.clear(); + _redis.spop(key, 3, std::back_inserter(rand_members)); + REDIS_ASSERT(rand_members.size() == 2, "failed to test srandmember"); + + rand_members.clear(); + _redis.srandmember(key, 2, std::back_inserter(rand_members)); + REDIS_ASSERT(rand_members.empty(), "failed to test srandmember with empty set"); + + _redis.spop(key, 2, std::back_inserter(rand_members)); + REDIS_ASSERT(rand_members.empty(), "failed to test spop with empty set"); + + _redis.sadd(key, members); + REDIS_ASSERT(_redis.srem(key, m1) == 1, "failed to test srem"); + REDIS_ASSERT(_redis.srem(key, members) == 2, "failed to test srem with mulitple members"); + REDIS_ASSERT(_redis.srem(key, members) == 0, "failed to test srem with mulitple members"); +} + +template +void SetCmdTest::_test_multi_set() { + auto k1 = test_key("s1"); + auto k2 = test_key("s2"); + auto k3 = test_key("s3"); + auto k4 = test_key("s4"); + auto k5 = test_key("s5"); + auto k6 = test_key("s6"); + + KeyDeleter keys(_redis, {k1, k2, k3, k4, k5, k6}); + + _redis.sadd(k1, {"a", "c"}); + _redis.sadd(k2, {"a", "b"}); + std::vector sdiff; + _redis.sdiff({k1, k1}, std::back_inserter(sdiff)); + REDIS_ASSERT(sdiff.empty(), "failed to test sdiff"); + + _redis.sdiff({k1, k2}, std::back_inserter(sdiff)); + REDIS_ASSERT(sdiff == std::vector({"c"}), "failed to test sdiff"); + + _redis.sdiffstore(k3, {k1, k2}); + sdiff.clear(); + _redis.smembers(k3, std::back_inserter(sdiff)); + REDIS_ASSERT(sdiff == std::vector({"c"}), "failed to test sdiffstore"); + + REDIS_ASSERT(_redis.sdiffstore(k3, k1) == 2, "failed to test sdiffstore"); + + REDIS_ASSERT(_redis.sinterstore(k3, k1) == 2, "failed to test sinterstore"); + + REDIS_ASSERT(_redis.sunionstore(k3, k1) == 2, "failed to test sunionstore"); + + std::vector sinter; + _redis.sinter({k1, k2}, std::back_inserter(sinter)); + REDIS_ASSERT(sinter == std::vector({"a"}), "failed to test sinter"); + + _redis.sinterstore(k4, {k1, k2}); + sinter.clear(); + _redis.smembers(k4, std::back_inserter(sinter)); + REDIS_ASSERT(sinter == std::vector({"a"}), "failed to test sinterstore"); + + std::unordered_set sunion; + _redis.sunion({k1, k2}, std::inserter(sunion, sunion.end())); + REDIS_ASSERT(sunion == std::unordered_set({"a", "b", "c"}), + "failed to test sunion"); + + _redis.sunionstore(k5, {k1, k2}); + sunion.clear(); + _redis.smembers(k5, std::inserter(sunion, sunion.end())); + REDIS_ASSERT(sunion == std::unordered_set({"a", "b", "c"}), + "failed to test sunionstore"); + + REDIS_ASSERT(_redis.smove(k5, k6, "a"), "failed to test smove"); +} + +template +void SetCmdTest::_test_sscan() { + auto key = test_key("sscan"); + + KeyDeleter deleter(_redis, key); + + std::unordered_set members = {"m1", "m2", "m3"}; + _redis.sadd(key, members.begin(), members.end()); + + std::unordered_set res; + long long cursor = 0; + while (true) { + cursor = _redis.sscan(key, cursor, "m*", 1, std::inserter(res, res.end())); + if (cursor == 0) { + break; + } + } + + REDIS_ASSERT(res == members, "failed to test sscan"); + + res.clear(); + cursor = 0; + while (true) { + cursor = _redis.sscan(key, cursor, std::inserter(res, res.end())); + if (cursor == 0) { + break; + } + } + + REDIS_ASSERT(res == members, "failed to test sscan"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/stream_cmds_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/stream_cmds_test.h new file mode 100644 index 000000000..24873a8b4 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/stream_cmds_test.h @@ -0,0 +1,54 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class StreamCmdsTest { +public: + explicit StreamCmdsTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + using Item = std::pair>; + using Result = std::unordered_map>; + + void _test_stream_cmds(); + + void _test_group_cmds(); + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "stream_cmds_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/stream_cmds_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/stream_cmds_test.hpp new file mode 100644 index 000000000..5926f2540 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/stream_cmds_test.hpp @@ -0,0 +1,255 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_HPP + +#include +#include +#include +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void StreamCmdsTest::run() { + _test_stream_cmds(); + + _test_group_cmds(); +} + +template +void StreamCmdsTest::_test_stream_cmds() { + auto key = test_key("stream"); + + KeyDeleter deleter(_redis, key); + + std::vector> attrs = { + {"f1", "v1"}, + {"f2", "v2"} + }; + const std::vector ids = {"1565427842-0", "1565427842-1"}; + REDIS_ASSERT(_redis.xadd(key, ids.at(0), attrs.begin(), attrs.end()) == ids.at(0), + "failed to test xadd"); + + std::vector> keys = {std::make_pair(key, "0-0")}; + Result result; + _redis.xread(keys.begin(), keys.end(), 1, std::inserter(result, result.end())); + + REDIS_ASSERT(result.size() == 1 + && result.find(key) != result.end() + && result[key].size() == 1 + && result[key].at(0).first == ids.at(0) + && result[key].at(0).second.size() == 2, + "failed to test xread"); + + result.clear(); + _redis.xread(key, std::string("0-0"), 1, std::inserter(result, result.end())); + + REDIS_ASSERT(result.size() == 1 + && result.find(key) != result.end() + && result[key].size() == 1 + && result[key].at(0).first == ids.at(0) + && result[key].at(0).second.size() == 2, + "failed to test xread"); + + result.clear(); + keys = {std::make_pair(key, ids.at(0))}; + _redis.xread(keys.begin(), + keys.end(), + std::chrono::seconds(1), + 2, + std::inserter(result, result.end())); + REDIS_ASSERT(result.size() == 0, "failed to test xread"); + + _redis.xread(key, + ids.at(0), + std::chrono::seconds(1), + 2, + std::inserter(result, result.end())); + REDIS_ASSERT(result.size() == 0, "failed to test xread"); + + REDIS_ASSERT(_redis.xadd(key, ids.at(1), + attrs.begin(), + attrs.end()) == ids.at(1), + "failed to test xadd"); + + REDIS_ASSERT(_redis.xlen(key) == 2, "failed to test xlen"); + + std::vector items; + _redis.xrange(key, "-", "+", 2, std::back_inserter(items)); + REDIS_ASSERT(items.size() == 2 && + items.at(0).first == ids.at(0) && + items.at(1).first == ids.at(1), + "failed to test xrange with count"); + + items.clear(); + _redis.xrange(key, "-", "+", 1, std::back_inserter(items)); + REDIS_ASSERT(items.size() == 1 && + items.at(0).first == ids.at(0), + "failed to test xrange with count"); + + items.clear(); + _redis.xrevrange(key, "+", "-", 2, std::back_inserter(items)); + REDIS_ASSERT(items.size() == 2 && + items.at(0).first == ids.at(1) && + items.at(1).first == ids.at(0), + "failed to test xrevrange with count"); + + items.clear(); + _redis.xrevrange(key, "+", "-", 1, std::back_inserter(items)); + REDIS_ASSERT(items.size() == 1 && + items.at(0).first == ids.at(1), + "failed to test xrevrange with count"); + + REDIS_ASSERT(_redis.xtrim(key, 1, false) == 1, "failed to test xtrim"); + + items.clear(); + _redis.xrange(key, "-", "+", std::back_inserter(items)); + REDIS_ASSERT(items.size() == 1 && items[0].first == ids.at(1), + "failed to test xrange"); + + items.clear(); + _redis.xrevrange(key, "+", "-", std::back_inserter(items)); + REDIS_ASSERT(items.size() == 1 && items[0].first == ids.at(1), + "failed to test xrevrange"); + + REDIS_ASSERT(_redis.xdel(key, {ids.at(1), std::string("111-111")}) == 1, + "failed to test xdel"); +} + +template +void StreamCmdsTest::_test_group_cmds() { + auto key = test_key("stream"); + + KeyDeleter deleter(_redis, key); + + auto group = "group"; + auto consumer1 = "consumer1"; + + _redis.xgroup_create(key, group, "$", true); + + std::vector> attrs = { + {"f1", "v1"}, + {"f2", "v2"} + }; + auto id = _redis.xadd(key, "*", attrs.begin(), attrs.end()); + auto keys = {std::make_pair(key, ">")}; + + Result result; + _redis.xreadgroup(group, + consumer1, + keys.begin(), + keys.end(), + 1, + std::inserter(result, result.end())); + REDIS_ASSERT(result.size() == 1 + && result.find(key) != result.end() + && result[key].size() == 1 + && result[key][0].first == id, + "failed to test xreadgroup"); + + result.clear(); + _redis.xreadgroup(group, + consumer1, + key, + std::string(">"), + 1, + std::inserter(result, result.end())); + REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup"); + + result.clear(); + _redis.xreadgroup(group, + "not-exist-consumer", + keys.begin(), + keys.end(), + 1, + std::inserter(result, result.end())); + REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup"); + + result.clear(); + _redis.xreadgroup(group, + consumer1, + keys.begin(), + keys.end(), + std::chrono::seconds(1), + 1, + std::inserter(result, result.end())); + REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup"); + + result.clear(); + _redis.xreadgroup(group, + consumer1, + key, + ">", + std::chrono::seconds(1), + 1, + std::inserter(result, result.end())); + REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup"); + + using PendingResult = std::vector>; + PendingResult pending_result; + _redis.xpending(key, group, "-", "+", 1, consumer1, std::back_inserter(pending_result)); + + REDIS_ASSERT(pending_result.size() == 1 + && std::get<0>(pending_result[0]) == id + && std::get<1>(pending_result[0]) == consumer1, + "failed to test xpending"); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + auto consumer2 = "consumer2"; + std::vector items; + auto ids = {id}; + _redis.xclaim(key, + group, + consumer2, + std::chrono::milliseconds(10), + ids, + std::back_inserter(items)); + REDIS_ASSERT(items.size() == 1 && items[0].first == id, "failed to test xclaim"); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + items.clear(); + _redis.xclaim(key, group, consumer1, std::chrono::milliseconds(10), id, std::back_inserter(items)); + REDIS_ASSERT(items.size() == 1 && items[0].first == id, "failed to test xclaim: " + std::to_string(items.size())); + + _redis.xack(key, group, id); + + REDIS_ASSERT(_redis.xgroup_delconsumer(key, group, consumer1) == 0, + "failed to test xgroup_delconsumer"); + + REDIS_ASSERT(_redis.xgroup_delconsumer(key, group, consumer2) == 0, + "failed to test xgroup_delconsumer"); + + REDIS_ASSERT(_redis.xgroup_destroy(key, group) == 1, + "failed to test xgroup_destroy"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/string_cmds_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/string_cmds_test.h new file mode 100644 index 000000000..86788b2bc --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/string_cmds_test.h @@ -0,0 +1,57 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class StringCmdTest { +public: + explicit StringCmdTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + void _test_str(); + + void _test_bit(); + + void _test_numeric(); + + void _test_getset(); + + void _test_mgetset(); + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "string_cmds_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/string_cmds_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/string_cmds_test.hpp new file mode 100644 index 000000000..cf702e433 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/string_cmds_test.hpp @@ -0,0 +1,247 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_HPP + +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void StringCmdTest::run() { + _test_str(); + + _test_bit(); + + _test_numeric(); + + _test_getset(); + + _test_mgetset(); +} + +template +void StringCmdTest::_test_str() { + auto key = test_key("str"); + + KeyDeleter deleter(_redis, key); + + std::string val("value"); + + long long val_size = val.size(); + + auto len1 = _redis.append(key, val); + REDIS_ASSERT(len1 == val_size, "failed to append to non-existent key"); + + auto len2 = _redis.append(key, val); + REDIS_ASSERT(len2 == len1 + val_size, "failed to append to non-empty string"); + + auto len3 = _redis.append(key, {}); + REDIS_ASSERT(len3 == len2, "failed to append empty string"); + + auto len4 = _redis.strlen(key); + REDIS_ASSERT(len4 == len3, "failed to test strlen"); + + REDIS_ASSERT(_redis.del(key) == 1, "failed to remove key"); + + auto len5 = _redis.append(key, {}); + REDIS_ASSERT(len5 == 0, "failed to append empty string to non-existent key"); + + _redis.del(key); + + REDIS_ASSERT(_redis.getrange(key, 0, 2) == "", "failed to test getrange on non-existent key"); + + _redis.set(key, val); + + REDIS_ASSERT(_redis.getrange(key, 1, 2) == val.substr(1, 2), "failed to test getrange"); + + long long new_size = val.size() * 2; + REDIS_ASSERT(_redis.setrange(key, val.size(), val) == new_size, "failed to test setrange"); + REDIS_ASSERT(_redis.getrange(key, 0, -1) == val + val, "failed to test setrange"); +} + +template +void StringCmdTest::_test_bit() { + auto key = test_key("bit"); + + KeyDeleter deleter(_redis, key); + + REDIS_ASSERT(_redis.bitcount(key) == 0, "failed to test bitcount on non-existent key"); + + REDIS_ASSERT(_redis.getbit(key, 5) == 0, "failed to test getbit"); + + REDIS_ASSERT(_redis.template command("SETBIT", key, 1, 1) == 0, + "failed to test setbit"); + REDIS_ASSERT(_redis.template command("SETBIT", key, 3, 1) == 0, + "failed to test setbit"); + REDIS_ASSERT(_redis.template command("SETBIT", key, 7, 1) == 0, + "failed to test setbit"); + REDIS_ASSERT(_redis.template command("SETBIT", key, 10, 1) == 0, + "failed to test setbit"); + REDIS_ASSERT(_redis.template command("SETBIT", key, 10, 0) == 1, + "failed to test setbit"); + REDIS_ASSERT(_redis.template command("SETBIT", key, 11, 1) == 0, + "failed to test setbit"); + REDIS_ASSERT(_redis.template command("SETBIT", key, 21, 1) == 0, + "failed to test setbit"); + + // key -> 01010001, 00010000, 00000100 + + REDIS_ASSERT(_redis.getbit(key, 1) == 1, "failed to test getbit"); + REDIS_ASSERT(_redis.getbit(key, 2) == 0, "failed to test getbit"); + REDIS_ASSERT(_redis.getbit(key, 7) == 1, "failed to test getbit"); + REDIS_ASSERT(_redis.getbit(key, 10) == 0, "failed to test getbit"); + REDIS_ASSERT(_redis.getbit(key, 100) == 0, "failed to test getbit"); + + REDIS_ASSERT(_redis.bitcount(key) == 5, "failed to test bitcount"); + REDIS_ASSERT(_redis.bitcount(key, 0, 0) == 3, "failed to test bitcount"); + REDIS_ASSERT(_redis.bitcount(key, 0, 1) == 4, "failed to test bitcount"); + REDIS_ASSERT(_redis.bitcount(key, -2, -1) == 2, "failed to test bitcount"); + + REDIS_ASSERT(_redis.bitpos(key, 1) == 1, "failed to test bitpos"); + REDIS_ASSERT(_redis.bitpos(key, 0) == 0, "failed to test bitpos"); + REDIS_ASSERT(_redis.bitpos(key, 1, 1, 1) == 11, "failed to test bitpos"); + REDIS_ASSERT(_redis.bitpos(key, 0, 1, 1) == 8, "failed to test bitpos"); + REDIS_ASSERT(_redis.bitpos(key, 1, -1, -1) == 21, "failed to test bitpos"); + REDIS_ASSERT(_redis.bitpos(key, 0, -1, -1) == 16, "failed to test bitpos"); + + auto dest_key = test_key("bitop_dest"); + auto src_key1 = test_key("bitop_src1"); + auto src_key2 = test_key("bitop_src2"); + + KeyDeleter deleters(_redis, {dest_key, src_key1, src_key2}); + + // src_key1 -> 00010000 + _redis.template command("SETBIT", src_key1, 3, 1); + + // src_key2 -> 00000000, 00001000 + _redis.template command("SETBIT", src_key2, 12, 1); + + REDIS_ASSERT(_redis.bitop(BitOp::AND, dest_key, {src_key1, src_key2}) == 2, + "failed to test bitop"); + + // dest_key -> 00000000, 00000000 + auto v = _redis.get(dest_key); + REDIS_ASSERT(v && *v == std::string(2, 0), "failed to test bitop"); + + REDIS_ASSERT(_redis.bitop(BitOp::NOT, dest_key, src_key1) == 1, + "failed to test bitop"); + + // dest_key -> 11101111 + v = _redis.get(dest_key); + REDIS_ASSERT(v && *v == std::string(1, '\xEF'), "failed to test bitop"); +} + +template +void StringCmdTest::_test_numeric() { + auto key = test_key("numeric"); + + KeyDeleter deleter(_redis, key); + + REDIS_ASSERT(_redis.incr(key) == 1, "failed to test incr"); + REDIS_ASSERT(_redis.decr(key) == 0, "failed to test decr"); + REDIS_ASSERT(_redis.incrby(key, 3) == 3, "failed to test incrby"); + REDIS_ASSERT(_redis.decrby(key, 3) == 0, "failed to test decrby"); + REDIS_ASSERT(_redis.incrby(key, -3) == -3, "failed to test incrby"); + REDIS_ASSERT(_redis.decrby(key, -3) == 0, "failed to test incrby"); + REDIS_ASSERT(_redis.incrbyfloat(key, 1.5) == 1.5, "failed to test incrbyfloat"); +} + +template +void StringCmdTest::_test_getset() { + auto key = test_key("getset"); + auto non_exist_key = test_key("non-existent"); + + KeyDeleter deleter(_redis, {key, non_exist_key}); + + std::string val("value"); + REDIS_ASSERT(_redis.set(key, val), "failed to test set"); + + auto v = _redis.get(key); + REDIS_ASSERT(v && *v == val, "failed to test get"); + + v = _redis.getset(key, val + val); + REDIS_ASSERT(v && *v == val, "failed to test get"); + + REDIS_ASSERT(!_redis.set(key, val, std::chrono::milliseconds(0), UpdateType::NOT_EXIST), + "failed to test set with NOT_EXIST type"); + REDIS_ASSERT(!_redis.set(non_exist_key, val, std::chrono::milliseconds(0), UpdateType::EXIST), + "failed to test set with EXIST type"); + + REDIS_ASSERT(!_redis.setnx(key, val), "failed to test setnx"); + REDIS_ASSERT(_redis.setnx(non_exist_key, val), "failed to test setnx"); + + auto ttl = std::chrono::seconds(10); + + _redis.set(key, val, ttl); + REDIS_ASSERT(_redis.ttl(key) <= ttl.count(), "failed to test set key with ttl"); + + _redis.setex(key, ttl, val); + REDIS_ASSERT(_redis.ttl(key) <= ttl.count(), "failed to test setex"); + + auto pttl = std::chrono::milliseconds(10000); + + _redis.psetex(key, ttl, val); + REDIS_ASSERT(_redis.pttl(key) <= pttl.count(), "failed to test psetex"); +} + +template +void StringCmdTest::_test_mgetset() { + auto kvs = {std::make_pair(test_key("k1"), "v1"), + std::make_pair(test_key("k2"), "v2"), + std::make_pair(test_key("k3"), "v3")}; + + std::vector keys; + std::vector vals; + for (const auto &kv : kvs) { + keys.push_back(kv.first); + vals.push_back(kv.second); + } + + KeyDeleter deleter(_redis, keys.begin(), keys.end()); + + _redis.mset(kvs); + + std::vector res; + _redis.mget(keys.begin(), keys.end(), std::back_inserter(res)); + + REDIS_ASSERT(res.size() == kvs.size(), "failed to test mget"); + + std::vector res_vals; + for (const auto &ele : res) { + REDIS_ASSERT(bool(ele), "failed to test mget"); + + res_vals.push_back(*ele); + } + + REDIS_ASSERT(vals == res_vals, "failed to test mget"); + + REDIS_ASSERT(!_redis.msetnx(kvs), "failed to test msetnx"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/test_main.cpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/test_main.cpp new file mode 100644 index 000000000..d52c7b7cc --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/test_main.cpp @@ -0,0 +1,433 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifdef _MSC_VER + +#include +#include + +#else + +#include // for getopt on non-Windows platform + +#endif + +#include +#include +#include +#include +#include +#include "sanity_test.h" +#include "connection_cmds_test.h" +#include "keys_cmds_test.h" +#include "string_cmds_test.h" +#include "list_cmds_test.h" +#include "hash_cmds_test.h" +#include "set_cmds_test.h" +#include "zset_cmds_test.h" +#include "hyperloglog_cmds_test.h" +#include "geo_cmds_test.h" +#include "script_cmds_test.h" +#include "pubsub_test.h" +#include "pipeline_transaction_test.h" +#include "threads_test.h" +#include "stream_cmds_test.h" +#include "benchmark_test.h" + +namespace { + +#ifdef _MSC_VER + +// A simple implementation of `getopt` on Windows platform. + +char *optarg = nullptr; +int optind = 1; + +int getopt(int argc, char **argv, const char *optstring); + +#endif + +struct TestOptions { + bool run_thread_test = false; +}; + +void print_help(); + +auto parse_options(int argc, char **argv) + -> std::tuple, + sw::redis::Optional, + sw::redis::Optional, + TestOptions>; + +template +void run_test(const sw::redis::ConnectionOptions &opts, const TestOptions &test_options); + +template +void run_benchmark(const sw::redis::ConnectionOptions &opts, + const sw::redis::test::BenchmarkOptions &benchmark_opts); + +} + +int main(int argc, char **argv) { + try { + sw::redis::Optional opts; + sw::redis::Optional cluster_node_opts; + sw::redis::Optional benchmark_opts; + TestOptions test_options; + std::tie(opts, cluster_node_opts, benchmark_opts, test_options) = parse_options(argc, argv); + + if (opts) { + std::cout << "Testing Redis..." << std::endl; + + if (benchmark_opts) { + run_benchmark(*opts, *benchmark_opts); + } else { + run_test(*opts, test_options); + } + } + + if (cluster_node_opts) { + std::cout << "Testing RedisCluster..." << std::endl; + + if (benchmark_opts) { + run_benchmark(*cluster_node_opts, *benchmark_opts); + } else { + run_test(*cluster_node_opts, test_options); + } + } + + std::cout << "Pass all tests" << std::endl; + } catch (const sw::redis::Error &e) { + std::cerr << "Test failed: " << e.what() << std::endl; + return -1; + } + + return 0; +} + +namespace { + +#ifdef _MSC_VER + +std::vector split(const std::string &str) { + if (str.empty()) { + return {}; + } + + std::vector result; + + std::string::size_type pos = 0; + std::string::size_type idx = 0; + while (true) { + pos = str.find(':', idx); + if (pos == std::string::npos) { + result.push_back(str.substr(idx)); + break; + } + + result.push_back(str.substr(idx, pos - idx)); + idx = pos + 1; + } + + return result; +} + +std::unordered_map parse_opt_map(const std::string &opts) { + auto fields = split(opts); + if (fields.empty()) { + return {}; + } + + std::unordered_map opt_map; + for (auto iter = fields.begin(); iter != fields.end() - 1; ++iter) { + const auto &field = *iter; + if (field.empty()) { + continue; + } + + for (auto it = field.begin(); it != field.end() - 1; ++it) { + opt_map.emplace(*it, false); + } + + opt_map.emplace(field.back(), true); + } + + const auto &last_opts = fields.back(); + if (!last_opts.empty()) { + for (auto c : last_opts) { + opt_map.emplace(c, false); + } + } + + return opt_map; +} + +int getopt(int argc, char **argv, const char *optstring) { + if (argc < 1 || argv == nullptr || optstring == nullptr || optind >= argc) { + return -1; + } + + auto opt_map = parse_opt_map(optstring); + + std::string opt = *(argv + optind); + if (opt.size() != 2 || opt.front() != '-') { + return -1; + } + + auto result = opt.back(); + auto iter = opt_map.find(result); + if (iter == opt_map.end()) { + return -1; + } + + ++optind; + + if (iter->second) { + if (optind == argc) { + return -1; + } + + optarg = *(argv + optind); + + ++optind; + } + + return result; +} + +#endif + +void print_help() { + std::cerr << "Usage: test_redis++ -h host -p port" + << " -n cluster_node -c cluster_port [-a auth] [-b] [-e key_prefix]\n\n"; + std::cerr << "See https://github.com/sewenew/redis-plus-plus#run-tests-optional" + << " for details on how to run test" << std::endl; +} + +auto parse_options(int argc, char **argv) + -> std::tuple, + sw::redis::Optional, + sw::redis::Optional, + TestOptions> { + std::string host; + int port = 0; + std::string auth; + std::string cluster_node; + int cluster_port = 0; + bool benchmark = false; + sw::redis::test::BenchmarkOptions tmp_benchmark_opts; + TestOptions test_options; + + int opt = 0; + while ((opt = getopt(argc, argv, "h:p:a:n:c:e:k:v:r:t:bs:m")) != -1) { + try { + switch (opt) { + case 'h': + host = optarg; + break; + + case 'p': + port = std::stoi(optarg); + break; + + case 'a': + auth = optarg; + break; + + case 'n': + cluster_node = optarg; + break; + + case 'c': + cluster_port = std::stoi(optarg); + break; + + case 'b': + benchmark = true; + break; + + case 'k': + tmp_benchmark_opts.key_len = std::stoi(optarg); + break; + + case 'v': + tmp_benchmark_opts.val_len = std::stoi(optarg); + break; + + case 'r': + tmp_benchmark_opts.total_request_num = std::stoi(optarg); + break; + + case 't': + tmp_benchmark_opts.thread_num = std::stoi(optarg); + break; + + case 's': + tmp_benchmark_opts.pool_size = std::stoi(optarg); + break; + + case 'm': + test_options.run_thread_test = true; + break; + + case 'e': + sw::redis::test::key_prefix(optarg); + break; + + default: + throw sw::redis::Error("Unknown command line option"); + break; + } + } catch (const sw::redis::Error &e) { + print_help(); + throw; + } catch (const std::exception &e) { + print_help(); + throw sw::redis::Error("Invalid command line option"); + } + } + + sw::redis::Optional opts; + if (!host.empty() && port > 0) { + sw::redis::ConnectionOptions tmp; + tmp.host = host; + tmp.port = port; + tmp.password = auth; + + opts = sw::redis::Optional(tmp); + } + + sw::redis::Optional cluster_opts; + if (!cluster_node.empty() && cluster_port > 0) { + sw::redis::ConnectionOptions tmp; + tmp.host = cluster_node; + tmp.port = cluster_port; + tmp.password = auth; + + cluster_opts = sw::redis::Optional(tmp); + } + + if (!opts && !cluster_opts) { + print_help(); + throw sw::redis::Error("Invalid connection options"); + } + + sw::redis::Optional benchmark_opts; + if (benchmark) { + benchmark_opts = sw::redis::Optional(tmp_benchmark_opts); + } + + return std::make_tuple(std::move(opts), std::move(cluster_opts), std::move(benchmark_opts), test_options); +} + +template +void run_test(const sw::redis::ConnectionOptions &opts, const TestOptions &test_options) { + auto instance = RedisInstance(opts); + + sw::redis::test::SanityTest sanity_test(opts, instance); + sanity_test.run(); + + std::cout << "Pass sanity tests" << std::endl; + + sw::redis::test::ConnectionCmdTest connection_test(instance); + connection_test.run(); + + std::cout << "Pass connection commands tests" << std::endl; + + sw::redis::test::KeysCmdTest keys_test(instance); + keys_test.run(); + + std::cout << "Pass keys commands tests" << std::endl; + + sw::redis::test::StringCmdTest string_test(instance); + string_test.run(); + + std::cout << "Pass string commands tests" << std::endl; + + sw::redis::test::ListCmdTest list_test(instance); + list_test.run(); + + std::cout << "Pass list commands tests" << std::endl; + + sw::redis::test::HashCmdTest hash_test(instance); + hash_test.run(); + + std::cout << "Pass hash commands tests" << std::endl; + + sw::redis::test::SetCmdTest set_test(instance); + set_test.run(); + + std::cout << "Pass set commands tests" << std::endl; + + sw::redis::test::ZSetCmdTest zset_test(instance); + zset_test.run(); + + std::cout << "Pass zset commands tests" << std::endl; + + sw::redis::test::HyperloglogCmdTest hll_test(instance); + hll_test.run(); + + std::cout << "Pass hyperloglog commands tests" << std::endl; + + sw::redis::test::GeoCmdTest geo_test(instance); + geo_test.run(); + + std::cout << "Pass geo commands tests" << std::endl; + + sw::redis::test::ScriptCmdTest script_test(instance); + script_test.run(); + + std::cout << "Pass script commands tests" << std::endl; + + sw::redis::test::PubSubTest pubsub_test(instance); + pubsub_test.run(); + + std::cout << "Pass pubsub tests" << std::endl; + + sw::redis::test::PipelineTransactionTest pipe_tx_test(instance); + pipe_tx_test.run(); + + std::cout << "Pass pipeline and transaction tests" << std::endl; + + if (test_options.run_thread_test) { + sw::redis::test::ThreadsTest threads_test(opts); + threads_test.run(); + + std::cout << "Pass threads tests" << std::endl; + } + + sw::redis::test::StreamCmdsTest stream_test(instance); + stream_test.run(); + + std::cout << "Pass stream commands tests" << std::endl; +} + +template +void run_benchmark(const sw::redis::ConnectionOptions &opts, + const sw::redis::test::BenchmarkOptions &benchmark_opts) { + std::cout << "Benchmark test options:" << std::endl; + std::cout << " Thread number: " << benchmark_opts.thread_num << std::endl; + std::cout << " Connection pool size: " << benchmark_opts.pool_size << std::endl; + std::cout << " Length of key: " << benchmark_opts.key_len << std::endl; + std::cout << " Length of value: " << benchmark_opts.val_len << std::endl; + + auto instance = RedisInstance(opts); + + sw::redis::test::BenchmarkTest benchmark_test(benchmark_opts, instance); + benchmark_test.run(); +} + +} diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/threads_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/threads_test.h new file mode 100644 index 000000000..aee307ec2 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/threads_test.h @@ -0,0 +1,51 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class ThreadsTest { +public: + explicit ThreadsTest(const ConnectionOptions &opts) : _opts(opts) {} + + void run(); + +private: + void _test_multithreads(RedisInstance redis, int threads_num, int times); + + void _test_timeout(); + + ConnectionOptions _opts; +}; + +} + +} + +} + +#include "threads_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/threads_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/threads_test.hpp new file mode 100644 index 000000000..24bee9454 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/threads_test.hpp @@ -0,0 +1,147 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_HPP + +#include +#include +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void ThreadsTest::run() { + // 100 * 10000 = 1 million writes + auto thread_num = 100; + auto times = 10000; + + // Default pool options: single connection and wait forever. + _test_multithreads(RedisInstance(_opts), thread_num, times); + + // Pool with 10 connections. + ConnectionPoolOptions pool_opts; + pool_opts.size = 10; + _test_multithreads(RedisInstance(_opts, pool_opts), thread_num, times); + + _test_timeout(); +} + +template +void ThreadsTest::_test_multithreads(RedisInstance redis, + int thread_num, + int times) { + std::vector keys; + keys.reserve(thread_num); + for (auto idx = 0; idx != thread_num; ++idx) { + auto key = test_key("multi-threads::" + std::to_string(idx)); + keys.push_back(key); + } + + using DeleterUPtr = std::unique_ptr>; + std::vector deleters; + for (const auto &key : keys) { + deleters.emplace_back(new KeyDeleter(redis, key)); + } + + std::vector workers; + workers.reserve(thread_num); + for (const auto &key : keys) { + workers.emplace_back([&redis, key, times]() { + try { + for (auto i = 0; i != times; ++i) { + redis.incr(key); + } + } catch (...) { + // Something bad happens. + return; + } + }); + } + + for (auto &worker : workers) { + worker.join(); + } + + for (const auto &key : keys) { + auto val = redis.get(key); + REDIS_ASSERT(bool(val), "failed to test multithreads, cannot get value of " + key); + + auto num = std::stoi(*val); + REDIS_ASSERT(num == times, "failed to test multithreads, num: " + + *val + ", times: " + std::to_string(times)); + } +} + +template +void ThreadsTest::_test_timeout() { + using namespace std::chrono; + + ConnectionPoolOptions pool_opts; + pool_opts.size = 1; + pool_opts.wait_timeout = milliseconds(100); + + auto redis = RedisInstance(_opts, pool_opts); + + auto key = test_key("key"); + + std::atomic slow_get_is_running{false}; + auto slow_get = [&slow_get_is_running](Connection &connection, const StringView &key) { + slow_get_is_running = true; + + // Sleep a while to simulate a slow get. + std::this_thread::sleep_for(seconds(5)); + + connection.send("GET %b", key.data(), key.size()); + }; + auto slow_get_thread = std::thread([&redis, slow_get, &key]() { + redis.command(slow_get, key); + }); + + auto get_thread = std::thread([&redis, &slow_get_is_running, &key]() { + try { + while (!slow_get_is_running) { + std::this_thread::sleep_for(milliseconds(10)); + } + + redis.get(key); + + // Slow get is running, this thread should + // timeout before obtaining the connection. + // So it never reaches here. + REDIS_ASSERT(false, "failed to test pool timeout"); + } catch (const Error &err) { + // This thread timeout. + } + }); + + slow_get_thread.join(); + get_thread.join(); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_HPP diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/utils.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/utils.h new file mode 100644 index 000000000..da4e1fdb4 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/utils.h @@ -0,0 +1,105 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_UTILS_H +#define SEWENEW_REDISPLUSPLUS_TEST_UTILS_H + +#include +#include +#include + +#define REDIS_ASSERT(condition, msg) \ + sw::redis::test::redis_assert((condition), (msg), __FILE__, __LINE__) + +namespace sw { + +namespace redis { + +namespace test { + +inline void redis_assert(bool condition, + const std::string &msg, + const std::string &file, + int line) { + if (!condition) { + auto err_msg = "ASSERT: " + msg + ". " + file + ":" + std::to_string(line); + throw Error(err_msg); + } +} + +inline std::string key_prefix(const std::string &key = "") { + static std::string KEY_PREFIX = "sw::redis::test"; + if (!key.empty()) { + KEY_PREFIX = key; + } + + return KEY_PREFIX; +} + +inline std::string test_key(const std::string &k) { + // Key prefix with hash tag, + // so that we can call multiple-key commands on RedisCluster. + return "{" + key_prefix() + "}::" + k; +} + +template +void cluster_specializing_test(Test &test, void (Test::*func)(Redis &instance), Redis &instance) { + (test.*func)(instance); +} + +template +void cluster_specializing_test(Test &test, + void (Test::*func)(Redis &instance), + RedisCluster &cluster) { + auto instance = cluster.redis("hash-tag"); + (test.*func)(instance); +} + +template +class KeyDeleter { +public: + template + KeyDeleter(RedisInstance &redis, Input first, Input last) : _redis(redis), _keys(first, last) { + _delete(); + } + + KeyDeleter(RedisInstance &redis, std::initializer_list il) : + KeyDeleter(redis, il.begin(), il.end()) {} + + KeyDeleter(RedisInstance &redis, const std::string &key) : KeyDeleter(redis, {key}) {} + + ~KeyDeleter() { + _delete(); + } + +private: + void _delete() { + if (!_keys.empty()) { + _redis.del(_keys.begin(), _keys.end()); + } + } + + RedisInstance &_redis; + std::vector _keys; +}; + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_UTILS_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/zset_cmds_test.h b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/zset_cmds_test.h new file mode 100644 index 000000000..56fd0b96f --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/zset_cmds_test.h @@ -0,0 +1,61 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_H +#define SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_H + +#include + +namespace sw { + +namespace redis { + +namespace test { + +template +class ZSetCmdTest { +public: + explicit ZSetCmdTest(RedisInstance &instance) : _redis(instance) {} + + void run(); + +private: + void _test_zset(); + + void _test_zscan(); + + void _test_range(); + + void _test_lex(); + + void _test_multi_zset(); + + void _test_zpop(); + + void _test_bzpop(); + + RedisInstance &_redis; +}; + +} + +} + +} + +#include "zset_cmds_test.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_H diff --git a/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/zset_cmds_test.hpp b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/zset_cmds_test.hpp new file mode 100644 index 000000000..9f6c05a08 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/test/src/sw/redis++/zset_cmds_test.hpp @@ -0,0 +1,422 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_HPP +#define SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_HPP + +#include +#include +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace test { + +template +void ZSetCmdTest::run() { + _test_zset(); + + _test_zscan(); + + _test_range(); + + _test_lex(); + + _test_multi_zset(); + + _test_zpop(); + + _test_bzpop(); +} + +template +void ZSetCmdTest::_test_zset() { + auto key = test_key("zset"); + + KeyDeleter deleter(_redis, key); + + std::map s = { + std::make_pair("m1", 1.2), + std::make_pair("m2", 2), + std::make_pair("m3", 3), + }; + + const auto &ele = *(s.begin()); + REDIS_ASSERT(_redis.zadd(key, ele.first, ele.second, UpdateType::EXIST) == 0, + "failed to test zadd with noexistent member"); + + REDIS_ASSERT(_redis.zadd(key, s.begin(), s.end()) == 3, "failed to test zadd"); + + REDIS_ASSERT(_redis.zadd(key, ele.first, ele.second, UpdateType::NOT_EXIST) == 0, + "failed to test zadd with exist member"); + + REDIS_ASSERT(_redis.zadd(key, s.begin(), s.end(), UpdateType::ALWAYS, true) == 0, + "failed to test zadd"); + + REDIS_ASSERT(_redis.zcard(key) == 3, "failed to test zcard"); + + auto rank = _redis.zrank(key, "m2"); + REDIS_ASSERT(bool(rank) && *rank == 1, "failed to test zrank"); + rank = _redis.zrevrank(key, "m4"); + REDIS_ASSERT(!rank, "failed to test zrevrank with nonexistent member"); + + auto score = _redis.zscore(key, "m4"); + REDIS_ASSERT(!score, "failed to test zscore with nonexistent member"); + + REDIS_ASSERT(_redis.zincrby(key, 1, "m3") == 4, "failed to test zincrby"); + + score = _redis.zscore(key, "m3"); + REDIS_ASSERT(score && *score == 4, "failed to test zscore"); + + REDIS_ASSERT(_redis.zrem(key, "m1") == 1, "failed to test zrem"); + REDIS_ASSERT(_redis.zrem(key, {"m1", "m2", "m3", "m4"}) == 2, "failed to test zrem"); +} + +template +void ZSetCmdTest::_test_zscan() { + auto key = test_key("zscan"); + + KeyDeleter deleter(_redis, key); + + std::map s = { + std::make_pair("m1", 1.2), + std::make_pair("m2", 2), + std::make_pair("m3", 3), + }; + _redis.zadd(key, s.begin(), s.end()); + + std::map res; + auto cursor = 0; + while (true) { + cursor = _redis.zscan(key, cursor, "m*", 2, std::inserter(res, res.end())); + if (cursor == 0) { + break; + } + } + REDIS_ASSERT(res == s, "failed to test zscan"); +} + +template +void ZSetCmdTest::_test_range() { + auto key = test_key("range"); + + KeyDeleter deleter(_redis, key); + + std::vector> s = { + std::make_pair("m1", 1), + std::make_pair("m2", 2), + std::make_pair("m3", 3), + std::make_pair("m4", 4), + }; + std::vector sKeys; + for (const auto &p : s) { + sKeys.push_back(p.first); + } + std::vector sReversedKeys = sKeys; + std::reverse(sReversedKeys.begin(), sReversedKeys.end()); + + _redis.zadd(key, s.begin(), s.end()); + + REDIS_ASSERT(_redis.zcount(key, UnboundedInterval{}) == static_cast(s.size()), + "failed to test zcount"); + + std::vector members; + _redis.zrange(key, 0, -1, std::back_inserter(members)); + REDIS_ASSERT(members.size() == s.size() && members == sKeys, + "failed to test zrange"); + + std::vector> res; + _redis.zrange(key, 0, -1, std::inserter(res, res.end())); + REDIS_ASSERT(s == res, "failed to test zrange with score"); + + members.clear(); + _redis.zrevrange(key, 0, 0, std::back_inserter(members)); + REDIS_ASSERT(members.size() == 1 && members.at(0) == sKeys.at(s.size()-1), + "failed to test zrevrange"); + + res.clear(); + _redis.zrevrange(key, 0, 0, std::inserter(res, res.end())); + REDIS_ASSERT(res.size() == 1 && res.at(0) == s.at(s.size()-1), + "failed to test zrevrange with score"); + + members.clear(); + _redis.zrangebyscore(key, UnboundedInterval{}, std::back_inserter(members)); + REDIS_ASSERT(members.size() == s.size() && members == sKeys, + "failed to test zrangebyscore"); + + LimitOptions limitOpts; + limitOpts.offset = 0; + limitOpts.count = 2; + members.clear(); + _redis.zrangebyscore(key, UnboundedInterval{}, limitOpts, std::back_inserter(members)); + REDIS_ASSERT(members.size() == 2 && + members.at(0) == sKeys.at(0) && + members.at(1) == sKeys.at(1), + "failed to test zrangebyscore with limits 0, 1"); + + limitOpts.offset = 1; + members.clear(); + _redis.zrangebyscore(key, UnboundedInterval{}, limitOpts, std::back_inserter(members)); + REDIS_ASSERT(members.size() == 2 && + members.at(0) == sKeys.at(1) && + members.at(1) == sKeys.at(2), + "failed to test zrangebyscore with limits 1, 2"); + + limitOpts.offset = s.size() - 1; + members.clear(); + _redis.zrangebyscore(key, UnboundedInterval{}, limitOpts, std::back_inserter(members)); + REDIS_ASSERT(members.size() == 1 && + members.at(0) == sKeys.at(sKeys.size() - 1), + "failed to test zrangebyscore with limits size-1, 2"); + + members.clear(); + _redis.zrangebyscore(key, + BoundedInterval(1, 2, BoundType::RIGHT_OPEN), + std::back_inserter(members)); + REDIS_ASSERT(members.size() == 1 && members.at(0) == sKeys.at(0), + "failed to test zrangebyscore"); + + res.clear(); + _redis.zrangebyscore(key, + LeftBoundedInterval(2, BoundType::OPEN), + std::inserter(res, res.end())); + REDIS_ASSERT(res.size() == 2 && res.at(0) == s.at(s.size()-2) && res.at(1) == s.at(s.size()-1), + "failed to test zrangebyscore"); + + members.clear(); + _redis.zrevrangebyscore(key, + BoundedInterval(1, 3, BoundType::CLOSED), + std::back_inserter(members)); + REDIS_ASSERT(members.size() == sReversedKeys.size()-1, + "failed to test zrevrangebyscore (size)"); + for (size_t i=0; i(1, 3, BoundType::CLOSED), + limitOpts, + std::back_inserter(members)); + REDIS_ASSERT(members.size() == 2 && + members.at(0) == sReversedKeys.at(1) && + members.at(1) == sReversedKeys.at(2), + "failed to test zrevrangebyscore with limits 0, 2"); + + limitOpts.offset = 1; + members.clear(); + _redis.zrevrangebyscore(key, + BoundedInterval(1, 3, BoundType::CLOSED), + limitOpts, + std::back_inserter(members)); + REDIS_ASSERT(members.size() == 2 && + members.at(0) == sReversedKeys.at(2) && + members.at(1) == sReversedKeys.at(3), + "failed to test zrevrangebyscore with limits 1, 2"); + + limitOpts.offset = s.size()-2; + members.clear(); + _redis.zrevrangebyscore(key, + BoundedInterval(1, 3, BoundType::CLOSED), + limitOpts, + std::back_inserter(members)); + REDIS_ASSERT(members.size() == 1 && + members.at(0) == sReversedKeys.at(s.size()-1), + "failed to test zrevrangebyscore with limits size-2, 2"); + + res.clear(); + _redis.zrevrangebyscore(key, + RightBoundedInterval(1, BoundType::LEFT_OPEN), + std::inserter(res, res.end())); + REDIS_ASSERT(res.size() == 1 && res.at(0) == s.at(0), "failed to test zrevrangebyscore"); + + REDIS_ASSERT(_redis.zremrangebyrank(key, 0, 0) == 1, "failed to test zremrangebyrank"); + + REDIS_ASSERT(_redis.zremrangebyscore(key, + BoundedInterval(2, 3, BoundType::LEFT_OPEN)) == 1, + "failed to test zremrangebyscore"); +} + +template +void ZSetCmdTest::_test_lex() { + auto key = test_key("lex"); + + KeyDeleter deleter(_redis, key); + + auto s = { + std::make_pair("m1", 0), + std::make_pair("m2", 0), + std::make_pair("m3", 0), + }; + _redis.zadd(key, s.begin(), s.end()); + + REDIS_ASSERT(_redis.zlexcount(key, UnboundedInterval{}) == 3, + "failed to test zlexcount"); + + std::vector members; + _redis.zrangebylex(key, + LeftBoundedInterval("m2", BoundType::OPEN), + std::back_inserter(members)); + REDIS_ASSERT(members.size() == 1 && members[0] == "m3", + "failed to test zrangebylex"); + + members.clear(); + _redis.zrevrangebylex(key, + RightBoundedInterval("m1", BoundType::LEFT_OPEN), + std::back_inserter(members)); + REDIS_ASSERT(members.size() == 1 && members[0] == "m1", + "failed to test zrevrangebylex"); + + REDIS_ASSERT(_redis.zremrangebylex(key, + BoundedInterval("m1", "m3", BoundType::OPEN)) == 1, + "failed to test zremrangebylex"); +} + +template +void ZSetCmdTest::_test_multi_zset() { + auto k1 = test_key("k1"); + auto k2 = test_key("k2"); + auto k3 = test_key("k3"); + + KeyDeleter deleter(_redis, {k1, k2, k3}); + + _redis.zadd(k1, {std::make_pair("a", 1), std::make_pair("b", 2)}); + _redis.zadd(k2, {std::make_pair("a", 2), std::make_pair("c", 3)}); + + REDIS_ASSERT(_redis.zinterstore(k3, {k1, k2}) == 1, "failed to test zinterstore"); + auto score = _redis.zscore(k3, "a"); + REDIS_ASSERT(bool(score) && *score == 3, "failed to test zinterstore"); + + REDIS_ASSERT(_redis.zinterstore(k3, k1, 2) == 2, "failed to test zinterstore"); + + _redis.del(k3); + + REDIS_ASSERT(_redis.zinterstore(k3, {k1, k2}, Aggregation::MAX) == 1, + "failed to test zinterstore"); + score = _redis.zscore(k3, "a"); + REDIS_ASSERT(bool(score) && *score == 2, "failed to test zinterstore"); + + _redis.del(k3); + + REDIS_ASSERT(_redis.zunionstore(k3, + {std::make_pair(k1, 1), std::make_pair(k2, 2)}, + Aggregation::MIN) == 3, + "failed to test zunionstore"); + std::vector> res; + _redis.zrange(k3, 0, -1, std::back_inserter(res)); + for (const auto &ele : res) { + if (ele.first == "a") { + REDIS_ASSERT(ele.second == 1, "failed to test zunionstore"); + } else if (ele.first == "b") { + REDIS_ASSERT(ele.second == 2, "failed to test zunionstore"); + } else if (ele.first == "c") { + REDIS_ASSERT(ele.second == 6, "failed to test zunionstore"); + } else { + REDIS_ASSERT(false, "failed to test zuionstore"); + } + } + + REDIS_ASSERT(_redis.zunionstore(k3, k1, 2) == 2, "failed to test zunionstore"); +} + +template +void ZSetCmdTest::_test_zpop() { + auto key = test_key("zpop"); + auto not_exist_key = test_key("zpop_not_exist"); + + KeyDeleter deleter(_redis, key); + + _redis.zadd(key, {std::make_pair("m1", 1.1), + std::make_pair("m2", 2.2), + std::make_pair("m3", 3.3), + std::make_pair("m4", 4.4), + std::make_pair("m5", 5.5), + std::make_pair("m6", 6.6)}); + + auto item = _redis.zpopmax(key); + REDIS_ASSERT(item && item->first == "m6", "failed to test zpopmax"); + + item = _redis.zpopmax(not_exist_key); + REDIS_ASSERT(!item, "failed to test zpopmax"); + + item = _redis.zpopmin(key); + REDIS_ASSERT(item && item->first == "m1", "failed to test zpopmin"); + + item = _redis.zpopmin(not_exist_key); + REDIS_ASSERT(!item, "failed to test zpopmin"); + + std::vector> vec; + _redis.zpopmax(key, 2, std::back_inserter(vec)); + REDIS_ASSERT(vec.size() == 2 && vec[0].first == "m5" && vec[1].first == "m4", + "failed to test zpopmax"); + + std::unordered_map m; + _redis.zpopmin(key, 2, std::inserter(m, m.end())); + REDIS_ASSERT(m.size() == 2 && m.find("m3") != m.end() && m.find("m2") != m.end(), + "failed to test zpopmin"); +} + +template +void ZSetCmdTest::_test_bzpop() { + auto key1 = test_key("bzpop1"); + auto key2 = test_key("bzpop2"); + + KeyDeleter deleter(_redis, {key1, key2}); + + _redis.zadd(key1, {std::make_pair("m1", 1.1), + std::make_pair("m2", 2.2), + std::make_pair("m3", 3.3), + std::make_pair("m4", 4.4), + std::make_pair("m5", 5.5), + std::make_pair("m6", 6.6)}); + + _redis.zadd(key2, {std::make_pair("m1", 1.1), + std::make_pair("m2", 2.2), + std::make_pair("m3", 3.3), + std::make_pair("m4", 4.4), + std::make_pair("m5", 5.5), + std::make_pair("m6", 6.6)}); + + auto item = _redis.bzpopmax(key1); + REDIS_ASSERT(item && std::get<0>(*item) == key1 && std::get<1>(*item) == "m6", + "failed to test bzpopmax"); + + item = _redis.bzpopmin(key1, std::chrono::seconds(1)); + REDIS_ASSERT(item && std::get<0>(*item) == key1 && std::get<1>(*item) == "m1", + "failed to test zpopmin"); + + item = _redis.bzpopmax({key1, key2}, std::chrono::seconds(1)); + REDIS_ASSERT(item && std::get<0>(*item) == key1 && std::get<1>(*item) == "m5", + "failed to test zpopmax"); + + item = _redis.bzpopmin({key2, key1}); + REDIS_ASSERT(item && std::get<0>(*item) == key2 && std::get<1>(*item) == "m1", + "failed to test zpopmin"); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_HPP diff --git a/make-linux.mk b/make-linux.mk index 145bebe52..69683d31b 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -302,9 +302,9 @@ endif ifeq ($(ZT_CONTROLLER),1) override CXXFLAGS+=-Wall -Wno-deprecated -std=c++17 -pthread $(INCLUDES) -DNDEBUG $(DEFS) - override LDLIBS+=-L/usr/pgsql-10/lib/ -lpqxx -lpq ext/hiredis-0.14.1/lib/centos8/libhiredis.a ext/redis-plus-plus-1.1.1/install/centos8/lib/libredis++.a -lssl -lcrypto + override LDLIBS+=-Lext/libpqxx-7.7.3/install/ubuntu22.04/lib -lpqxx -lpq ext/hiredis-1.0.2/lib/ubuntu22.04/libhiredis.a ext/redis-plus-plus-1.3.3/install/ubuntu22.04/lib/libredis++.a -lssl -lcrypto override DEFS+=-DZT_CONTROLLER_USE_LIBPQ - override INCLUDES+=-I/usr/pgsql-10/include -Iext/hiredis-0.14.1/include/ -Iext/redis-plus-plus-1.1.1/install/centos8/include/sw/ + override INCLUDES+=-I/usr/include/postgresql -Iext/libpqxx-7.7.3/install/ubuntu22.04/include -Iext/hiredis-1.0.2/include/ -Iext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/ endif # ARM32 hell -- use conservative CFLAGS

v^zfv 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 RB-EGXv(8mWp9{_ZKLCSuw1fZv literal 0 HcmV?d00001 diff --git a/ext/hiredis-1.0.2/net.c b/ext/hiredis-1.0.2/net.c new file mode 100644 index 000000000..c6b0e5d8e --- /dev/null +++ b/ext/hiredis-1.0.2/net.c @@ -0,0 +1,612 @@ +/* Extracted from anet.c to work properly with Hiredis error reporting. + * + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2014, Pieter Noordhuis + * Copyright (c) 2015, Matt Stancliff , + * Jan-Erik Rediger + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "net.h" +#include "sds.h" +#include "sockcompat.h" +#include "win32.h" + +/* Defined in hiredis.c */ +void __redisSetError(redisContext *c, int type, const char *str); + +void redisNetClose(redisContext *c) { + if (c && c->fd != REDIS_INVALID_FD) { + close(c->fd); + c->fd = REDIS_INVALID_FD; + } +} + +ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) { + ssize_t nread = recv(c->fd, buf, bufcap, 0); + if (nread == -1) { + if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { + /* Try again later */ + return 0; + } else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) { + /* especially in windows */ + __redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout"); + return -1; + } else { + __redisSetError(c, REDIS_ERR_IO, NULL); + return -1; + } + } else if (nread == 0) { + __redisSetError(c, REDIS_ERR_EOF, "Server closed the connection"); + return -1; + } else { + return nread; + } +} + +ssize_t redisNetWrite(redisContext *c) { + ssize_t nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0); + if (nwritten < 0) { + if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { + /* Try again later */ + } else { + __redisSetError(c, REDIS_ERR_IO, NULL); + return -1; + } + } + return nwritten; +} + +static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) { + int errorno = errno; /* snprintf() may change errno */ + char buf[128] = { 0 }; + size_t len = 0; + + if (prefix != NULL) + len = snprintf(buf,sizeof(buf),"%s: ",prefix); + strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len); + __redisSetError(c,type,buf); +} + +static int redisSetReuseAddr(redisContext *c) { + int on = 1; + if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + redisNetClose(c); + return REDIS_ERR; + } + return REDIS_OK; +} + +static int redisCreateSocket(redisContext *c, int type) { + redisFD s; + if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + return REDIS_ERR; + } + c->fd = s; + if (type == AF_INET) { + if (redisSetReuseAddr(c) == REDIS_ERR) { + return REDIS_ERR; + } + } + return REDIS_OK; +} + +static int redisSetBlocking(redisContext *c, int blocking) { +#ifndef _WIN32 + int flags; + + /* Set the socket nonblocking. + * Note that fcntl(2) for F_GETFL and F_SETFL can't be + * interrupted by a signal. */ + if ((flags = fcntl(c->fd, F_GETFL)) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)"); + redisNetClose(c); + return REDIS_ERR; + } + + if (blocking) + flags &= ~O_NONBLOCK; + else + flags |= O_NONBLOCK; + + if (fcntl(c->fd, F_SETFL, flags) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)"); + redisNetClose(c); + return REDIS_ERR; + } +#else + u_long mode = blocking ? 0 : 1; + if (ioctl(c->fd, FIONBIO, &mode) == -1) { + __redisSetErrorFromErrno(c, REDIS_ERR_IO, "ioctl(FIONBIO)"); + redisNetClose(c); + return REDIS_ERR; + } +#endif /* _WIN32 */ + return REDIS_OK; +} + +int redisKeepAlive(redisContext *c, int interval) { + int val = 1; + redisFD fd = c->fd; + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){ + __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); + return REDIS_ERR; + } + + val = interval; + +#if defined(__APPLE__) && defined(__MACH__) + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) { + __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); + return REDIS_ERR; + } +#else +#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__) + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) { + __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); + return REDIS_ERR; + } + + val = interval/3; + if (val == 0) val = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) { + __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); + return REDIS_ERR; + } + + val = 3; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) { + __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); + return REDIS_ERR; + } +#endif +#endif + + return REDIS_OK; +} + +int redisSetTcpNoDelay(redisContext *c) { + int yes = 1; + if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)"); + redisNetClose(c); + return REDIS_ERR; + } + return REDIS_OK; +} + +#define __MAX_MSEC (((LONG_MAX) - 999) / 1000) + +static int redisContextTimeoutMsec(redisContext *c, long *result) +{ + const struct timeval *timeout = c->connect_timeout; + long msec = -1; + + /* Only use timeout when not NULL. */ + if (timeout != NULL) { + if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) { + *result = msec; + return REDIS_ERR; + } + + msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000); + + if (msec < 0 || msec > INT_MAX) { + msec = INT_MAX; + } + } + + *result = msec; + return REDIS_OK; +} + +static int redisContextWaitReady(redisContext *c, long msec) { + struct pollfd wfd[1]; + + wfd[0].fd = c->fd; + wfd[0].events = POLLOUT; + + if (errno == EINPROGRESS) { + int res; + + if ((res = poll(wfd, 1, msec)) == -1) { + __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)"); + redisNetClose(c); + return REDIS_ERR; + } else if (res == 0) { + errno = ETIMEDOUT; + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + redisNetClose(c); + return REDIS_ERR; + } + + if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) { + redisCheckSocketError(c); + return REDIS_ERR; + } + + return REDIS_OK; + } + + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + redisNetClose(c); + return REDIS_ERR; +} + +int redisCheckConnectDone(redisContext *c, int *completed) { + int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen); + if (rc == 0) { + *completed = 1; + return REDIS_OK; + } + switch (errno) { + case EISCONN: + *completed = 1; + return REDIS_OK; + case EALREADY: + case EINPROGRESS: + case EWOULDBLOCK: + *completed = 0; + return REDIS_OK; + default: + return REDIS_ERR; + } +} + +int redisCheckSocketError(redisContext *c) { + int err = 0, errno_saved = errno; + socklen_t errlen = sizeof(err); + + if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)"); + return REDIS_ERR; + } + + if (err == 0) { + err = errno_saved; + } + + if (err) { + errno = err; + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + return REDIS_ERR; + } + + return REDIS_OK; +} + +int redisContextSetTimeout(redisContext *c, const struct timeval tv) { + const void *to_ptr = &tv; + size_t to_sz = sizeof(tv); + + if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)"); + return REDIS_ERR; + } + if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)"); + return REDIS_ERR; + } + return REDIS_OK; +} + +int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) { + /* Same timeval struct, short circuit */ + if (c->connect_timeout == timeout) + return REDIS_OK; + + /* Allocate context timeval if we need to */ + if (c->connect_timeout == NULL) { + c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout)); + if (c->connect_timeout == NULL) + return REDIS_ERR; + } + + memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout)); + return REDIS_OK; +} + +int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) { + /* Same timeval struct, short circuit */ + if (c->command_timeout == timeout) + return REDIS_OK; + + /* Allocate context timeval if we need to */ + if (c->command_timeout == NULL) { + c->command_timeout = hi_malloc(sizeof(*c->command_timeout)); + if (c->command_timeout == NULL) + return REDIS_ERR; + } + + memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout)); + return REDIS_OK; +} + +static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, + const struct timeval *timeout, + const char *source_addr) { + redisFD s; + int rv, n; + char _port[6]; /* strlen("65535"); */ + struct addrinfo hints, *servinfo, *bservinfo, *p, *b; + int blocking = (c->flags & REDIS_BLOCK); + int reuseaddr = (c->flags & REDIS_REUSEADDR); + int reuses = 0; + long timeout_msec = -1; + + servinfo = NULL; + c->connection_type = REDIS_CONN_TCP; + c->tcp.port = port; + + /* We need to take possession of the passed parameters + * to make them reusable for a reconnect. + * We also carefully check we don't free data we already own, + * as in the case of the reconnect method. + * + * This is a bit ugly, but atleast it works and doesn't leak memory. + **/ + if (c->tcp.host != addr) { + hi_free(c->tcp.host); + + c->tcp.host = hi_strdup(addr); + if (c->tcp.host == NULL) + goto oom; + } + + if (timeout) { + if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR) + goto oom; + } else { + hi_free(c->connect_timeout); + c->connect_timeout = NULL; + } + + if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) { + __redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified"); + goto error; + } + + if (source_addr == NULL) { + hi_free(c->tcp.source_addr); + c->tcp.source_addr = NULL; + } else if (c->tcp.source_addr != source_addr) { + hi_free(c->tcp.source_addr); + c->tcp.source_addr = hi_strdup(source_addr); + } + + snprintf(_port, 6, "%d", port); + memset(&hints,0,sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + /* Try with IPv6 if no IPv4 address was found. We do it in this order since + * in a Redis client you can't afford to test if you have IPv6 connectivity + * as this would add latency to every connect. Otherwise a more sensible + * route could be: Use IPv6 if both addresses are available and there is IPv6 + * connectivity. */ + if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) { + hints.ai_family = AF_INET6; + if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) { + __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv)); + return REDIS_ERR; + } + } + for (p = servinfo; p != NULL; p = p->ai_next) { +addrretry: + if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD) + continue; + + c->fd = s; + if (redisSetBlocking(c,0) != REDIS_OK) + goto error; + if (c->tcp.source_addr) { + int bound = 0; + /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */ + if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) { + char buf[128]; + snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv)); + __redisSetError(c,REDIS_ERR_OTHER,buf); + goto error; + } + + if (reuseaddr) { + n = 1; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n, + sizeof(n)) < 0) { + freeaddrinfo(bservinfo); + goto error; + } + } + + for (b = bservinfo; b != NULL; b = b->ai_next) { + if (bind(s,b->ai_addr,b->ai_addrlen) != -1) { + bound = 1; + break; + } + } + freeaddrinfo(bservinfo); + if (!bound) { + char buf[128]; + snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno)); + __redisSetError(c,REDIS_ERR_OTHER,buf); + goto error; + } + } + + /* For repeat connection */ + hi_free(c->saddr); + c->saddr = hi_malloc(p->ai_addrlen); + if (c->saddr == NULL) + goto oom; + + memcpy(c->saddr, p->ai_addr, p->ai_addrlen); + c->addrlen = p->ai_addrlen; + + if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { + if (errno == EHOSTUNREACH) { + redisNetClose(c); + continue; + } else if (errno == EINPROGRESS) { + if (blocking) { + goto wait_for_ready; + } + /* This is ok. + * Note that even when it's in blocking mode, we unset blocking + * for `connect()` + */ + } else if (errno == EADDRNOTAVAIL && reuseaddr) { + if (++reuses >= REDIS_CONNECT_RETRIES) { + goto error; + } else { + redisNetClose(c); + goto addrretry; + } + } else { + wait_for_ready: + if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) + goto error; + if (redisSetTcpNoDelay(c) != REDIS_OK) + goto error; + } + } + if (blocking && redisSetBlocking(c,1) != REDIS_OK) + goto error; + + c->flags |= REDIS_CONNECTED; + rv = REDIS_OK; + goto end; + } + if (p == NULL) { + char buf[128]; + snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno)); + __redisSetError(c,REDIS_ERR_OTHER,buf); + goto error; + } + +oom: + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); +error: + rv = REDIS_ERR; +end: + if(servinfo) { + freeaddrinfo(servinfo); + } + + return rv; // Need to return REDIS_OK if alright +} + +int redisContextConnectTcp(redisContext *c, const char *addr, int port, + const struct timeval *timeout) { + return _redisContextConnectTcp(c, addr, port, timeout, NULL); +} + +int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, + const struct timeval *timeout, + const char *source_addr) { + return _redisContextConnectTcp(c, addr, port, timeout, source_addr); +} + +int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) { +#ifndef _WIN32 + int blocking = (c->flags & REDIS_BLOCK); + struct sockaddr_un *sa; + long timeout_msec = -1; + + if (redisCreateSocket(c,AF_UNIX) < 0) + return REDIS_ERR; + if (redisSetBlocking(c,0) != REDIS_OK) + return REDIS_ERR; + + c->connection_type = REDIS_CONN_UNIX; + if (c->unix_sock.path != path) { + hi_free(c->unix_sock.path); + + c->unix_sock.path = hi_strdup(path); + if (c->unix_sock.path == NULL) + goto oom; + } + + if (timeout) { + if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR) + goto oom; + } else { + hi_free(c->connect_timeout); + c->connect_timeout = NULL; + } + + if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK) + return REDIS_ERR; + + /* Don't leak sockaddr if we're reconnecting */ + if (c->saddr) hi_free(c->saddr); + + sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un))); + if (sa == NULL) + goto oom; + + c->addrlen = sizeof(struct sockaddr_un); + sa->sun_family = AF_UNIX; + strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1); + if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) { + if (errno == EINPROGRESS && !blocking) { + /* This is ok. */ + } else { + if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) + return REDIS_ERR; + } + } + + /* Reset socket to be blocking after connect(2). */ + if (blocking && redisSetBlocking(c,1) != REDIS_OK) + return REDIS_ERR; + + c->flags |= REDIS_CONNECTED; + return REDIS_OK; +#else + /* We currently do not support Unix sockets for Windows. */ + /* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */ + errno = EPROTONOSUPPORT; + return REDIS_ERR; +#endif /* _WIN32 */ +oom: + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; +} diff --git a/ext/hiredis-1.0.2/net.h b/ext/hiredis-1.0.2/net.h new file mode 100644 index 000000000..9f43283a5 --- /dev/null +++ b/ext/hiredis-1.0.2/net.h @@ -0,0 +1,56 @@ +/* Extracted from anet.c to work properly with Hiredis error reporting. + * + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2014, Pieter Noordhuis + * Copyright (c) 2015, Matt Stancliff , + * Jan-Erik Rediger + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __NET_H +#define __NET_H + +#include "hiredis.h" + +void redisNetClose(redisContext *c); +ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap); +ssize_t redisNetWrite(redisContext *c); + +int redisCheckSocketError(redisContext *c); +int redisContextSetTimeout(redisContext *c, const struct timeval tv); +int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); +int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, + const struct timeval *timeout, + const char *source_addr); +int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); +int redisKeepAlive(redisContext *c, int interval); +int redisCheckConnectDone(redisContext *c, int *completed); + +int redisSetTcpNoDelay(redisContext *c); + +#endif diff --git a/ext/hiredis-1.0.2/read.c b/ext/hiredis-1.0.2/read.c new file mode 100644 index 000000000..09524692b --- /dev/null +++ b/ext/hiredis-1.0.2/read.c @@ -0,0 +1,739 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include +#include +#ifndef _MSC_VER +#include +#include +#endif +#include +#include +#include +#include +#include + +#include "alloc.h" +#include "read.h" +#include "sds.h" +#include "win32.h" + +/* Initial size of our nested reply stack and how much we grow it when needd */ +#define REDIS_READER_STACK_SIZE 9 + +static void __redisReaderSetError(redisReader *r, int type, const char *str) { + size_t len; + + if (r->reply != NULL && r->fn && r->fn->freeObject) { + r->fn->freeObject(r->reply); + r->reply = NULL; + } + + /* Clear input buffer on errors. */ + sdsfree(r->buf); + r->buf = NULL; + r->pos = r->len = 0; + + /* Reset task stack. */ + r->ridx = -1; + + /* Set error. */ + r->err = type; + len = strlen(str); + len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1); + memcpy(r->errstr,str,len); + r->errstr[len] = '\0'; +} + +static size_t chrtos(char *buf, size_t size, char byte) { + size_t len = 0; + + switch(byte) { + case '\\': + case '"': + len = snprintf(buf,size,"\"\\%c\"",byte); + break; + case '\n': len = snprintf(buf,size,"\"\\n\""); break; + case '\r': len = snprintf(buf,size,"\"\\r\""); break; + case '\t': len = snprintf(buf,size,"\"\\t\""); break; + case '\a': len = snprintf(buf,size,"\"\\a\""); break; + case '\b': len = snprintf(buf,size,"\"\\b\""); break; + default: + if (isprint(byte)) + len = snprintf(buf,size,"\"%c\"",byte); + else + len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte); + break; + } + + return len; +} + +static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) { + char cbuf[8], sbuf[128]; + + chrtos(cbuf,sizeof(cbuf),byte); + snprintf(sbuf,sizeof(sbuf), + "Protocol error, got %s as reply type byte", cbuf); + __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf); +} + +static void __redisReaderSetErrorOOM(redisReader *r) { + __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory"); +} + +static char *readBytes(redisReader *r, unsigned int bytes) { + char *p; + if (r->len-r->pos >= bytes) { + p = r->buf+r->pos; + r->pos += bytes; + return p; + } + return NULL; +} + +/* Find pointer to \r\n. */ +static char *seekNewline(char *s, size_t len) { + int pos = 0; + int _len = len-1; + + /* Position should be < len-1 because the character at "pos" should be + * followed by a \n. Note that strchr cannot be used because it doesn't + * allow to search a limited length and the buffer that is being searched + * might not have a trailing NULL character. */ + while (pos < _len) { + while(pos < _len && s[pos] != '\r') pos++; + if (pos==_len) { + /* Not found. */ + return NULL; + } else { + if (s[pos+1] == '\n') { + /* Found. */ + return s+pos; + } else { + /* Continue searching. */ + pos++; + } + } + } + return NULL; +} + +/* Convert a string into a long long. Returns REDIS_OK if the string could be + * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value + * will be set to the parsed value when appropriate. + * + * Note that this function demands that the string strictly represents + * a long long: no spaces or other characters before or after the string + * representing the number are accepted, nor zeroes at the start if not + * for the string "0" representing the zero number. + * + * Because of its strictness, it is safe to use this function to check if + * you can convert a string into a long long, and obtain back the string + * from the number without any loss in the string representation. */ +static int string2ll(const char *s, size_t slen, long long *value) { + const char *p = s; + size_t plen = 0; + int negative = 0; + unsigned long long v; + + if (plen == slen) + return REDIS_ERR; + + /* Special case: first and only digit is 0. */ + if (slen == 1 && p[0] == '0') { + if (value != NULL) *value = 0; + return REDIS_OK; + } + + if (p[0] == '-') { + negative = 1; + p++; plen++; + + /* Abort on only a negative sign. */ + if (plen == slen) + return REDIS_ERR; + } + + /* First digit should be 1-9, otherwise the string should just be 0. */ + if (p[0] >= '1' && p[0] <= '9') { + v = p[0]-'0'; + p++; plen++; + } else if (p[0] == '0' && slen == 1) { + *value = 0; + return REDIS_OK; + } else { + return REDIS_ERR; + } + + while (plen < slen && p[0] >= '0' && p[0] <= '9') { + if (v > (ULLONG_MAX / 10)) /* Overflow. */ + return REDIS_ERR; + v *= 10; + + if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */ + return REDIS_ERR; + v += p[0]-'0'; + + p++; plen++; + } + + /* Return if not all bytes were used. */ + if (plen < slen) + return REDIS_ERR; + + if (negative) { + if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */ + return REDIS_ERR; + if (value != NULL) *value = -v; + } else { + if (v > LLONG_MAX) /* Overflow. */ + return REDIS_ERR; + if (value != NULL) *value = v; + } + return REDIS_OK; +} + +static char *readLine(redisReader *r, int *_len) { + char *p, *s; + int len; + + p = r->buf+r->pos; + s = seekNewline(p,(r->len-r->pos)); + if (s != NULL) { + len = s-(r->buf+r->pos); + r->pos += len+2; /* skip \r\n */ + if (_len) *_len = len; + return p; + } + return NULL; +} + +static void moveToNextTask(redisReader *r) { + redisReadTask *cur, *prv; + while (r->ridx >= 0) { + /* Return a.s.a.p. when the stack is now empty. */ + if (r->ridx == 0) { + r->ridx--; + return; + } + + cur = r->task[r->ridx]; + prv = r->task[r->ridx-1]; + assert(prv->type == REDIS_REPLY_ARRAY || + prv->type == REDIS_REPLY_MAP || + prv->type == REDIS_REPLY_SET || + prv->type == REDIS_REPLY_PUSH); + if (cur->idx == prv->elements-1) { + r->ridx--; + } else { + /* Reset the type because the next item can be anything */ + assert(cur->idx < prv->elements); + cur->type = -1; + cur->elements = -1; + cur->idx++; + return; + } + } +} + +static int processLineItem(redisReader *r) { + redisReadTask *cur = r->task[r->ridx]; + void *obj; + char *p; + int len; + + if ((p = readLine(r,&len)) != NULL) { + if (cur->type == REDIS_REPLY_INTEGER) { + if (r->fn && r->fn->createInteger) { + long long v; + if (string2ll(p, len, &v) == REDIS_ERR) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad integer value"); + return REDIS_ERR; + } + obj = r->fn->createInteger(cur,v); + } else { + obj = (void*)REDIS_REPLY_INTEGER; + } + } else if (cur->type == REDIS_REPLY_DOUBLE) { + if (r->fn && r->fn->createDouble) { + char buf[326], *eptr; + double d; + + if ((size_t)len >= sizeof(buf)) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Double value is too large"); + return REDIS_ERR; + } + + memcpy(buf,p,len); + buf[len] = '\0'; + + if (strcasecmp(buf,",inf") == 0) { + d = INFINITY; /* Positive infinite. */ + } else if (strcasecmp(buf,",-inf") == 0) { + d = -INFINITY; /* Negative infinite. */ + } else { + d = strtod((char*)buf,&eptr); + if (buf[0] == '\0' || eptr[0] != '\0' || isnan(d)) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad double value"); + return REDIS_ERR; + } + } + obj = r->fn->createDouble(cur,d,buf,len); + } else { + obj = (void*)REDIS_REPLY_DOUBLE; + } + } else if (cur->type == REDIS_REPLY_NIL) { + if (r->fn && r->fn->createNil) + obj = r->fn->createNil(cur); + else + obj = (void*)REDIS_REPLY_NIL; + } else if (cur->type == REDIS_REPLY_BOOL) { + int bval = p[0] == 't' || p[0] == 'T'; + if (r->fn && r->fn->createBool) + obj = r->fn->createBool(cur,bval); + else + obj = (void*)REDIS_REPLY_BOOL; + } else { + /* Type will be error or status. */ + if (r->fn && r->fn->createString) + obj = r->fn->createString(cur,p,len); + else + obj = (void*)(size_t)(cur->type); + } + + if (obj == NULL) { + __redisReaderSetErrorOOM(r); + return REDIS_ERR; + } + + /* Set reply if this is the root object. */ + if (r->ridx == 0) r->reply = obj; + moveToNextTask(r); + return REDIS_OK; + } + + return REDIS_ERR; +} + +static int processBulkItem(redisReader *r) { + redisReadTask *cur = r->task[r->ridx]; + void *obj = NULL; + char *p, *s; + long long len; + unsigned long bytelen; + int success = 0; + + p = r->buf+r->pos; + s = seekNewline(p,r->len-r->pos); + if (s != NULL) { + p = r->buf+r->pos; + bytelen = s-(r->buf+r->pos)+2; /* include \r\n */ + + if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad bulk string length"); + return REDIS_ERR; + } + + if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bulk string length out of range"); + return REDIS_ERR; + } + + if (len == -1) { + /* The nil object can always be created. */ + if (r->fn && r->fn->createNil) + obj = r->fn->createNil(cur); + else + obj = (void*)REDIS_REPLY_NIL; + success = 1; + } else { + /* Only continue when the buffer contains the entire bulk item. */ + bytelen += len+2; /* include \r\n */ + if (r->pos+bytelen <= r->len) { + if ((cur->type == REDIS_REPLY_VERB && len < 4) || + (cur->type == REDIS_REPLY_VERB && s[5] != ':')) + { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Verbatim string 4 bytes of content type are " + "missing or incorrectly encoded."); + return REDIS_ERR; + } + if (r->fn && r->fn->createString) + obj = r->fn->createString(cur,s+2,len); + else + obj = (void*)(long)cur->type; + success = 1; + } + } + + /* Proceed when obj was created. */ + if (success) { + if (obj == NULL) { + __redisReaderSetErrorOOM(r); + return REDIS_ERR; + } + + r->pos += bytelen; + + /* Set reply if this is the root object. */ + if (r->ridx == 0) r->reply = obj; + moveToNextTask(r); + return REDIS_OK; + } + } + + return REDIS_ERR; +} + +static int redisReaderGrow(redisReader *r) { + redisReadTask **aux; + int newlen; + + /* Grow our stack size */ + newlen = r->tasks + REDIS_READER_STACK_SIZE; + aux = hi_realloc(r->task, sizeof(*r->task) * newlen); + if (aux == NULL) + goto oom; + + r->task = aux; + + /* Allocate new tasks */ + for (; r->tasks < newlen; r->tasks++) { + r->task[r->tasks] = hi_calloc(1, sizeof(**r->task)); + if (r->task[r->tasks] == NULL) + goto oom; + } + + return REDIS_OK; +oom: + __redisReaderSetErrorOOM(r); + return REDIS_ERR; +} + +/* Process the array, map and set types. */ +static int processAggregateItem(redisReader *r) { + redisReadTask *cur = r->task[r->ridx]; + void *obj; + char *p; + long long elements; + int root = 0, len; + + /* Set error for nested multi bulks with depth > 7 */ + if (r->ridx == r->tasks - 1) { + if (redisReaderGrow(r) == REDIS_ERR) + return REDIS_ERR; + } + + if ((p = readLine(r,&len)) != NULL) { + if (string2ll(p, len, &elements) == REDIS_ERR) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad multi-bulk length"); + return REDIS_ERR; + } + + root = (r->ridx == 0); + + if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) || + (r->maxelements > 0 && elements > r->maxelements)) + { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Multi-bulk length out of range"); + return REDIS_ERR; + } + + if (elements == -1) { + if (r->fn && r->fn->createNil) + obj = r->fn->createNil(cur); + else + obj = (void*)REDIS_REPLY_NIL; + + if (obj == NULL) { + __redisReaderSetErrorOOM(r); + return REDIS_ERR; + } + + moveToNextTask(r); + } else { + if (cur->type == REDIS_REPLY_MAP) elements *= 2; + + if (r->fn && r->fn->createArray) + obj = r->fn->createArray(cur,elements); + else + obj = (void*)(long)cur->type; + + if (obj == NULL) { + __redisReaderSetErrorOOM(r); + return REDIS_ERR; + } + + /* Modify task stack when there are more than 0 elements. */ + if (elements > 0) { + cur->elements = elements; + cur->obj = obj; + r->ridx++; + r->task[r->ridx]->type = -1; + r->task[r->ridx]->elements = -1; + r->task[r->ridx]->idx = 0; + r->task[r->ridx]->obj = NULL; + r->task[r->ridx]->parent = cur; + r->task[r->ridx]->privdata = r->privdata; + } else { + moveToNextTask(r); + } + } + + /* Set reply if this is the root object. */ + if (root) r->reply = obj; + return REDIS_OK; + } + + return REDIS_ERR; +} + +static int processItem(redisReader *r) { + redisReadTask *cur = r->task[r->ridx]; + char *p; + + /* check if we need to read type */ + if (cur->type < 0) { + if ((p = readBytes(r,1)) != NULL) { + switch (p[0]) { + case '-': + cur->type = REDIS_REPLY_ERROR; + break; + case '+': + cur->type = REDIS_REPLY_STATUS; + break; + case ':': + cur->type = REDIS_REPLY_INTEGER; + break; + case ',': + cur->type = REDIS_REPLY_DOUBLE; + break; + case '_': + cur->type = REDIS_REPLY_NIL; + break; + case '$': + cur->type = REDIS_REPLY_STRING; + break; + case '*': + cur->type = REDIS_REPLY_ARRAY; + break; + case '%': + cur->type = REDIS_REPLY_MAP; + break; + case '~': + cur->type = REDIS_REPLY_SET; + break; + case '#': + cur->type = REDIS_REPLY_BOOL; + break; + case '=': + cur->type = REDIS_REPLY_VERB; + break; + case '>': + cur->type = REDIS_REPLY_PUSH; + break; + default: + __redisReaderSetErrorProtocolByte(r,*p); + return REDIS_ERR; + } + } else { + /* could not consume 1 byte */ + return REDIS_ERR; + } + } + + /* process typed item */ + switch(cur->type) { + case REDIS_REPLY_ERROR: + case REDIS_REPLY_STATUS: + case REDIS_REPLY_INTEGER: + case REDIS_REPLY_DOUBLE: + case REDIS_REPLY_NIL: + case REDIS_REPLY_BOOL: + return processLineItem(r); + case REDIS_REPLY_STRING: + case REDIS_REPLY_VERB: + return processBulkItem(r); + case REDIS_REPLY_ARRAY: + case REDIS_REPLY_MAP: + case REDIS_REPLY_SET: + case REDIS_REPLY_PUSH: + return processAggregateItem(r); + default: + assert(NULL); + return REDIS_ERR; /* Avoid warning. */ + } +} + +redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) { + redisReader *r; + + r = hi_calloc(1,sizeof(redisReader)); + if (r == NULL) + return NULL; + + r->buf = sdsempty(); + if (r->buf == NULL) + goto oom; + + r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task)); + if (r->task == NULL) + goto oom; + + for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) { + r->task[r->tasks] = hi_calloc(1, sizeof(**r->task)); + if (r->task[r->tasks] == NULL) + goto oom; + } + + r->fn = fn; + r->maxbuf = REDIS_READER_MAX_BUF; + r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS; + r->ridx = -1; + + return r; +oom: + redisReaderFree(r); + return NULL; +} + +void redisReaderFree(redisReader *r) { + if (r == NULL) + return; + + if (r->reply != NULL && r->fn && r->fn->freeObject) + r->fn->freeObject(r->reply); + + if (r->task) { + /* We know r->task[i] is allocated if i < r->tasks */ + for (int i = 0; i < r->tasks; i++) { + hi_free(r->task[i]); + } + + hi_free(r->task); + } + + sdsfree(r->buf); + hi_free(r); +} + +int redisReaderFeed(redisReader *r, const char *buf, size_t len) { + sds newbuf; + + /* Return early when this reader is in an erroneous state. */ + if (r->err) + return REDIS_ERR; + + /* Copy the provided buffer. */ + if (buf != NULL && len >= 1) { + /* Destroy internal buffer when it is empty and is quite large. */ + if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) { + sdsfree(r->buf); + r->buf = sdsempty(); + if (r->buf == 0) goto oom; + + r->pos = 0; + } + + newbuf = sdscatlen(r->buf,buf,len); + if (newbuf == NULL) goto oom; + + r->buf = newbuf; + r->len = sdslen(r->buf); + } + + return REDIS_OK; +oom: + __redisReaderSetErrorOOM(r); + return REDIS_ERR; +} + +int redisReaderGetReply(redisReader *r, void **reply) { + /* Default target pointer to NULL. */ + if (reply != NULL) + *reply = NULL; + + /* Return early when this reader is in an erroneous state. */ + if (r->err) + return REDIS_ERR; + + /* When the buffer is empty, there will never be a reply. */ + if (r->len == 0) + return REDIS_OK; + + /* Set first item to process when the stack is empty. */ + if (r->ridx == -1) { + r->task[0]->type = -1; + r->task[0]->elements = -1; + r->task[0]->idx = -1; + r->task[0]->obj = NULL; + r->task[0]->parent = NULL; + r->task[0]->privdata = r->privdata; + r->ridx = 0; + } + + /* Process items in reply. */ + while (r->ridx >= 0) + if (processItem(r) != REDIS_OK) + break; + + /* Return ASAP when an error occurred. */ + if (r->err) + return REDIS_ERR; + + /* Discard part of the buffer when we've consumed at least 1k, to avoid + * doing unnecessary calls to memmove() in sds.c. */ + if (r->pos >= 1024) { + if (sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR; + r->pos = 0; + r->len = sdslen(r->buf); + } + + /* Emit a reply when there is one. */ + if (r->ridx == -1) { + if (reply != NULL) { + *reply = r->reply; + } else if (r->reply != NULL && r->fn && r->fn->freeObject) { + r->fn->freeObject(r->reply); + } + r->reply = NULL; + } + return REDIS_OK; +} diff --git a/ext/hiredis-1.0.2/read.h b/ext/hiredis-1.0.2/read.h new file mode 100644 index 000000000..2d74d77a5 --- /dev/null +++ b/ext/hiredis-1.0.2/read.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __HIREDIS_READ_H +#define __HIREDIS_READ_H +#include /* for size_t */ + +#define REDIS_ERR -1 +#define REDIS_OK 0 + +/* When an error occurs, the err flag in a context is set to hold the type of + * error that occurred. REDIS_ERR_IO means there was an I/O error and you + * should use the "errno" variable to find out what is wrong. + * For other values, the "errstr" field will hold a description. */ +#define REDIS_ERR_IO 1 /* Error in read or write */ +#define REDIS_ERR_EOF 3 /* End of file */ +#define REDIS_ERR_PROTOCOL 4 /* Protocol error */ +#define REDIS_ERR_OOM 5 /* Out of memory */ +#define REDIS_ERR_TIMEOUT 6 /* Timed out */ +#define REDIS_ERR_OTHER 2 /* Everything else... */ + +#define REDIS_REPLY_STRING 1 +#define REDIS_REPLY_ARRAY 2 +#define REDIS_REPLY_INTEGER 3 +#define REDIS_REPLY_NIL 4 +#define REDIS_REPLY_STATUS 5 +#define REDIS_REPLY_ERROR 6 +#define REDIS_REPLY_DOUBLE 7 +#define REDIS_REPLY_BOOL 8 +#define REDIS_REPLY_MAP 9 +#define REDIS_REPLY_SET 10 +#define REDIS_REPLY_ATTR 11 +#define REDIS_REPLY_PUSH 12 +#define REDIS_REPLY_BIGNUM 13 +#define REDIS_REPLY_VERB 14 + +/* Default max unused reader buffer. */ +#define REDIS_READER_MAX_BUF (1024*16) + +/* Default multi-bulk element limit */ +#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct redisReadTask { + int type; + long long elements; /* number of elements in multibulk container */ + int idx; /* index in parent (array) object */ + void *obj; /* holds user-generated value for a read task */ + struct redisReadTask *parent; /* parent task */ + void *privdata; /* user-settable arbitrary field */ +} redisReadTask; + +typedef struct redisReplyObjectFunctions { + void *(*createString)(const redisReadTask*, char*, size_t); + void *(*createArray)(const redisReadTask*, size_t); + void *(*createInteger)(const redisReadTask*, long long); + void *(*createDouble)(const redisReadTask*, double, char*, size_t); + void *(*createNil)(const redisReadTask*); + void *(*createBool)(const redisReadTask*, int); + void (*freeObject)(void*); +} redisReplyObjectFunctions; + +typedef struct redisReader { + int err; /* Error flags, 0 when there is no error */ + char errstr[128]; /* String representation of error when applicable */ + + char *buf; /* Read buffer */ + size_t pos; /* Buffer cursor */ + size_t len; /* Buffer length */ + size_t maxbuf; /* Max length of unused buffer */ + long long maxelements; /* Max multi-bulk elements */ + + redisReadTask **task; + int tasks; + + int ridx; /* Index of current read task */ + void *reply; /* Temporary reply pointer */ + + redisReplyObjectFunctions *fn; + void *privdata; +} redisReader; + +/* Public API for the protocol parser. */ +redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn); +void redisReaderFree(redisReader *r); +int redisReaderFeed(redisReader *r, const char *buf, size_t len); +int redisReaderGetReply(redisReader *r, void **reply); + +#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) +#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply) +#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/hiredis-1.0.2/sds.c b/ext/hiredis-1.0.2/sds.c new file mode 100644 index 000000000..49d2096b7 --- /dev/null +++ b/ext/hiredis-1.0.2/sds.c @@ -0,0 +1,1289 @@ +/* SDSLib 2.0 -- A C dynamic strings library + * + * Copyright (c) 2006-2015, Salvatore Sanfilippo + * Copyright (c) 2015, Oran Agra + * Copyright (c) 2015, Redis Labs, Inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include +#include +#include +#include +#include +#include +#include "sds.h" +#include "sdsalloc.h" + +static inline int sdsHdrSize(char type) { + switch(type&SDS_TYPE_MASK) { + case SDS_TYPE_5: + return sizeof(struct sdshdr5); + case SDS_TYPE_8: + return sizeof(struct sdshdr8); + case SDS_TYPE_16: + return sizeof(struct sdshdr16); + case SDS_TYPE_32: + return sizeof(struct sdshdr32); + case SDS_TYPE_64: + return sizeof(struct sdshdr64); + } + return 0; +} + +static inline char sdsReqType(size_t string_size) { + if (string_size < 32) + return SDS_TYPE_5; + if (string_size < 0xff) + return SDS_TYPE_8; + if (string_size < 0xffff) + return SDS_TYPE_16; + if (string_size < 0xffffffff) + return SDS_TYPE_32; + return SDS_TYPE_64; +} + +/* Create a new sds string with the content specified by the 'init' pointer + * and 'initlen'. + * If NULL is used for 'init' the string is initialized with zero bytes. + * + * The string is always null-termined (all the sds strings are, always) so + * even if you create an sds string with: + * + * mystring = sdsnewlen("abc",3); + * + * You can print the string with printf() as there is an implicit \0 at the + * end of the string. However the string is binary safe and can contain + * \0 characters in the middle, as the length is stored in the sds header. */ +sds sdsnewlen(const void *init, size_t initlen) { + void *sh; + sds s; + char type = sdsReqType(initlen); + /* Empty strings are usually created in order to append. Use type 8 + * since type 5 is not good at this. */ + if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; + int hdrlen = sdsHdrSize(type); + unsigned char *fp; /* flags pointer. */ + + sh = s_malloc(hdrlen+initlen+1); + if (sh == NULL) return NULL; + if (!init) + memset(sh, 0, hdrlen+initlen+1); + s = (char*)sh+hdrlen; + fp = ((unsigned char*)s)-1; + switch(type) { + case SDS_TYPE_5: { + *fp = type | (initlen << SDS_TYPE_BITS); + break; + } + case SDS_TYPE_8: { + SDS_HDR_VAR(8,s); + sh->len = initlen; + sh->alloc = initlen; + *fp = type; + break; + } + case SDS_TYPE_16: { + SDS_HDR_VAR(16,s); + sh->len = initlen; + sh->alloc = initlen; + *fp = type; + break; + } + case SDS_TYPE_32: { + SDS_HDR_VAR(32,s); + sh->len = initlen; + sh->alloc = initlen; + *fp = type; + break; + } + case SDS_TYPE_64: { + SDS_HDR_VAR(64,s); + sh->len = initlen; + sh->alloc = initlen; + *fp = type; + break; + } + } + if (initlen && init) + memcpy(s, init, initlen); + s[initlen] = '\0'; + return s; +} + +/* Create an empty (zero length) sds string. Even in this case the string + * always has an implicit null term. */ +sds sdsempty(void) { + return sdsnewlen("",0); +} + +/* Create a new sds string starting from a null terminated C string. */ +sds sdsnew(const char *init) { + size_t initlen = (init == NULL) ? 0 : strlen(init); + return sdsnewlen(init, initlen); +} + +/* Duplicate an sds string. */ +sds sdsdup(const sds s) { + return sdsnewlen(s, sdslen(s)); +} + +/* Free an sds string. No operation is performed if 's' is NULL. */ +void sdsfree(sds s) { + if (s == NULL) return; + s_free((char*)s-sdsHdrSize(s[-1])); +} + +/* Set the sds string length to the length as obtained with strlen(), so + * considering as content only up to the first null term character. + * + * This function is useful when the sds string is hacked manually in some + * way, like in the following example: + * + * s = sdsnew("foobar"); + * s[2] = '\0'; + * sdsupdatelen(s); + * printf("%d\n", sdslen(s)); + * + * The output will be "2", but if we comment out the call to sdsupdatelen() + * the output will be "6" as the string was modified but the logical length + * remains 6 bytes. */ +void sdsupdatelen(sds s) { + int reallen = strlen(s); + sdssetlen(s, reallen); +} + +/* Modify an sds string in-place to make it empty (zero length). + * However all the existing buffer is not discarded but set as free space + * so that next append operations will not require allocations up to the + * number of bytes previously available. */ +void sdsclear(sds s) { + sdssetlen(s, 0); + s[0] = '\0'; +} + +/* Enlarge the free space at the end of the sds string so that the caller + * is sure that after calling this function can overwrite up to addlen + * bytes after the end of the string, plus one more byte for nul term. + * + * Note: this does not change the *length* of the sds string as returned + * by sdslen(), but only the free buffer space we have. */ +sds sdsMakeRoomFor(sds s, size_t addlen) { + void *sh, *newsh; + size_t avail = sdsavail(s); + size_t len, newlen; + char type, oldtype = s[-1] & SDS_TYPE_MASK; + int hdrlen; + + /* Return ASAP if there is enough space left. */ + if (avail >= addlen) return s; + + len = sdslen(s); + sh = (char*)s-sdsHdrSize(oldtype); + newlen = (len+addlen); + if (newlen < SDS_MAX_PREALLOC) + newlen *= 2; + else + newlen += SDS_MAX_PREALLOC; + + type = sdsReqType(newlen); + + /* Don't use type 5: the user is appending to the string and type 5 is + * not able to remember empty space, so sdsMakeRoomFor() must be called + * at every appending operation. */ + if (type == SDS_TYPE_5) type = SDS_TYPE_8; + + hdrlen = sdsHdrSize(type); + if (oldtype==type) { + newsh = s_realloc(sh, hdrlen+newlen+1); + if (newsh == NULL) return NULL; + s = (char*)newsh+hdrlen; + } else { + /* Since the header size changes, need to move the string forward, + * and can't use realloc */ + newsh = s_malloc(hdrlen+newlen+1); + if (newsh == NULL) return NULL; + memcpy((char*)newsh+hdrlen, s, len+1); + s_free(sh); + s = (char*)newsh+hdrlen; + s[-1] = type; + sdssetlen(s, len); + } + sdssetalloc(s, newlen); + return s; +} + +/* Reallocate the sds string so that it has no free space at the end. The + * contained string remains not altered, but next concatenation operations + * will require a reallocation. + * + * After the call, the passed sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdsRemoveFreeSpace(sds s) { + void *sh, *newsh; + char type, oldtype = s[-1] & SDS_TYPE_MASK; + int hdrlen; + size_t len = sdslen(s); + sh = (char*)s-sdsHdrSize(oldtype); + + type = sdsReqType(len); + hdrlen = sdsHdrSize(type); + if (oldtype==type) { + newsh = s_realloc(sh, hdrlen+len+1); + if (newsh == NULL) return NULL; + s = (char*)newsh+hdrlen; + } else { + newsh = s_malloc(hdrlen+len+1); + if (newsh == NULL) return NULL; + memcpy((char*)newsh+hdrlen, s, len+1); + s_free(sh); + s = (char*)newsh+hdrlen; + s[-1] = type; + sdssetlen(s, len); + } + sdssetalloc(s, len); + return s; +} + +/* Return the total size of the allocation of the specifed sds string, + * including: + * 1) The sds header before the pointer. + * 2) The string. + * 3) The free buffer at the end if any. + * 4) The implicit null term. + */ +size_t sdsAllocSize(sds s) { + size_t alloc = sdsalloc(s); + return sdsHdrSize(s[-1])+alloc+1; +} + +/* Return the pointer of the actual SDS allocation (normally SDS strings + * are referenced by the start of the string buffer). */ +void *sdsAllocPtr(sds s) { + return (void*) (s-sdsHdrSize(s[-1])); +} + +/* Increment the sds length and decrements the left free space at the + * end of the string according to 'incr'. Also set the null term + * in the new end of the string. + * + * This function is used in order to fix the string length after the + * user calls sdsMakeRoomFor(), writes something after the end of + * the current string, and finally needs to set the new length. + * + * Note: it is possible to use a negative increment in order to + * right-trim the string. + * + * Usage example: + * + * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the + * following schema, to cat bytes coming from the kernel to the end of an + * sds string without copying into an intermediate buffer: + * + * oldlen = sdslen(s); + * s = sdsMakeRoomFor(s, BUFFER_SIZE); + * nread = read(fd, s+oldlen, BUFFER_SIZE); + * ... check for nread <= 0 and handle it ... + * sdsIncrLen(s, nread); + */ +void sdsIncrLen(sds s, int incr) { + unsigned char flags = s[-1]; + size_t len; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: { + unsigned char *fp = ((unsigned char*)s)-1; + unsigned char oldlen = SDS_TYPE_5_LEN(flags); + assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr))); + *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS); + len = oldlen+incr; + break; + } + case SDS_TYPE_8: { + SDS_HDR_VAR(8,s); + assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); + len = (sh->len += incr); + break; + } + case SDS_TYPE_16: { + SDS_HDR_VAR(16,s); + assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); + len = (sh->len += incr); + break; + } + case SDS_TYPE_32: { + SDS_HDR_VAR(32,s); + assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); + len = (sh->len += incr); + break; + } + case SDS_TYPE_64: { + SDS_HDR_VAR(64,s); + assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr))); + len = (sh->len += incr); + break; + } + default: len = 0; /* Just to avoid compilation warnings. */ + } + s[len] = '\0'; +} + +/* Grow the sds to have the specified length. Bytes that were not part of + * the original length of the sds will be set to zero. + * + * if the specified length is smaller than the current length, no operation + * is performed. */ +sds sdsgrowzero(sds s, size_t len) { + size_t curlen = sdslen(s); + + if (len <= curlen) return s; + s = sdsMakeRoomFor(s,len-curlen); + if (s == NULL) return NULL; + + /* Make sure added region doesn't contain garbage */ + memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ + sdssetlen(s, len); + return s; +} + +/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the + * end of the specified sds string 's'. + * + * After the call, the passed sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscatlen(sds s, const void *t, size_t len) { + size_t curlen = sdslen(s); + + s = sdsMakeRoomFor(s,len); + if (s == NULL) return NULL; + memcpy(s+curlen, t, len); + sdssetlen(s, curlen+len); + s[curlen+len] = '\0'; + return s; +} + +/* Append the specified null termianted C string to the sds string 's'. + * + * After the call, the passed sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscat(sds s, const char *t) { + return sdscatlen(s, t, strlen(t)); +} + +/* Append the specified sds 't' to the existing sds 's'. + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscatsds(sds s, const sds t) { + return sdscatlen(s, t, sdslen(t)); +} + +/* Destructively modify the sds string 's' to hold the specified binary + * safe string pointed by 't' of length 'len' bytes. */ +sds sdscpylen(sds s, const char *t, size_t len) { + if (sdsalloc(s) < len) { + s = sdsMakeRoomFor(s,len-sdslen(s)); + if (s == NULL) return NULL; + } + memcpy(s, t, len); + s[len] = '\0'; + sdssetlen(s, len); + return s; +} + +/* Like sdscpylen() but 't' must be a null-termined string so that the length + * of the string is obtained with strlen(). */ +sds sdscpy(sds s, const char *t) { + return sdscpylen(s, t, strlen(t)); +} + +/* Helper for sdscatlonglong() doing the actual number -> string + * conversion. 's' must point to a string with room for at least + * SDS_LLSTR_SIZE bytes. + * + * The function returns the length of the null-terminated string + * representation stored at 's'. */ +#define SDS_LLSTR_SIZE 21 +int sdsll2str(char *s, long long value) { + char *p, aux; + unsigned long long v; + size_t l; + + /* Generate the string representation, this method produces + * an reversed string. */ + v = (value < 0) ? -value : value; + p = s; + do { + *p++ = '0'+(v%10); + v /= 10; + } while(v); + if (value < 0) *p++ = '-'; + + /* Compute length and add null term. */ + l = p-s; + *p = '\0'; + + /* Reverse the string. */ + p--; + while(s < p) { + aux = *s; + *s = *p; + *p = aux; + s++; + p--; + } + return l; +} + +/* Identical sdsll2str(), but for unsigned long long type. */ +int sdsull2str(char *s, unsigned long long v) { + char *p, aux; + size_t l; + + /* Generate the string representation, this method produces + * an reversed string. */ + p = s; + do { + *p++ = '0'+(v%10); + v /= 10; + } while(v); + + /* Compute length and add null term. */ + l = p-s; + *p = '\0'; + + /* Reverse the string. */ + p--; + while(s < p) { + aux = *s; + *s = *p; + *p = aux; + s++; + p--; + } + return l; +} + +/* Create an sds string from a long long value. It is much faster than: + * + * sdscatprintf(sdsempty(),"%lld\n", value); + */ +sds sdsfromlonglong(long long value) { + char buf[SDS_LLSTR_SIZE]; + int len = sdsll2str(buf,value); + + return sdsnewlen(buf,len); +} + +/* Like sdscatprintf() but gets va_list instead of being variadic. */ +sds sdscatvprintf(sds s, const char *fmt, va_list ap) { + va_list cpy; + char staticbuf[1024], *buf = staticbuf, *t; + size_t buflen = strlen(fmt)*2; + + /* We try to start using a static buffer for speed. + * If not possible we revert to heap allocation. */ + if (buflen > sizeof(staticbuf)) { + buf = s_malloc(buflen); + if (buf == NULL) return NULL; + } else { + buflen = sizeof(staticbuf); + } + + /* Try with buffers two times bigger every time we fail to + * fit the string in the current buffer size. */ + while(1) { + buf[buflen-2] = '\0'; + va_copy(cpy,ap); + vsnprintf(buf, buflen, fmt, cpy); + va_end(cpy); + if (buf[buflen-2] != '\0') { + if (buf != staticbuf) s_free(buf); + buflen *= 2; + buf = s_malloc(buflen); + if (buf == NULL) return NULL; + continue; + } + break; + } + + /* Finally concat the obtained string to the SDS string and return it. */ + t = sdscat(s, buf); + if (buf != staticbuf) s_free(buf); + return t; +} + +/* Append to the sds string 's' a string obtained using printf-alike format + * specifier. + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. + * + * Example: + * + * s = sdsnew("Sum is: "); + * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). + * + * Often you need to create a string from scratch with the printf-alike + * format. When this is the need, just use sdsempty() as the target string: + * + * s = sdscatprintf(sdsempty(), "... your format ...", args); + */ +sds sdscatprintf(sds s, const char *fmt, ...) { + va_list ap; + char *t; + va_start(ap, fmt); + t = sdscatvprintf(s,fmt,ap); + va_end(ap); + return t; +} + +/* This function is similar to sdscatprintf, but much faster as it does + * not rely on sprintf() family functions implemented by the libc that + * are often very slow. Moreover directly handling the sds string as + * new data is concatenated provides a performance improvement. + * + * However this function only handles an incompatible subset of printf-alike + * format specifiers: + * + * %s - C String + * %S - SDS string + * %i - signed int + * %I - 64 bit signed integer (long long, int64_t) + * %u - unsigned int + * %U - 64 bit unsigned integer (unsigned long long, uint64_t) + * %% - Verbatim "%" character. + */ +sds sdscatfmt(sds s, char const *fmt, ...) { + const char *f = fmt; + int i; + va_list ap; + + va_start(ap,fmt); + i = sdslen(s); /* Position of the next byte to write to dest str. */ + while(*f) { + char next, *str; + size_t l; + long long num; + unsigned long long unum; + + /* Make sure there is always space for at least 1 char. */ + if (sdsavail(s)==0) { + s = sdsMakeRoomFor(s,1); + if (s == NULL) goto fmt_error; + } + + switch(*f) { + case '%': + next = *(f+1); + f++; + switch(next) { + case 's': + case 'S': + str = va_arg(ap,char*); + l = (next == 's') ? strlen(str) : sdslen(str); + if (sdsavail(s) < l) { + s = sdsMakeRoomFor(s,l); + if (s == NULL) goto fmt_error; + } + memcpy(s+i,str,l); + sdsinclen(s,l); + i += l; + break; + case 'i': + case 'I': + if (next == 'i') + num = va_arg(ap,int); + else + num = va_arg(ap,long long); + { + char buf[SDS_LLSTR_SIZE]; + l = sdsll2str(buf,num); + if (sdsavail(s) < l) { + s = sdsMakeRoomFor(s,l); + if (s == NULL) goto fmt_error; + } + memcpy(s+i,buf,l); + sdsinclen(s,l); + i += l; + } + break; + case 'u': + case 'U': + if (next == 'u') + unum = va_arg(ap,unsigned int); + else + unum = va_arg(ap,unsigned long long); + { + char buf[SDS_LLSTR_SIZE]; + l = sdsull2str(buf,unum); + if (sdsavail(s) < l) { + s = sdsMakeRoomFor(s,l); + if (s == NULL) goto fmt_error; + } + memcpy(s+i,buf,l); + sdsinclen(s,l); + i += l; + } + break; + default: /* Handle %% and generally %. */ + s[i++] = next; + sdsinclen(s,1); + break; + } + break; + default: + s[i++] = *f; + sdsinclen(s,1); + break; + } + f++; + } + va_end(ap); + + /* Add null-term */ + s[i] = '\0'; + return s; + +fmt_error: + va_end(ap); + return NULL; +} + +/* Remove the part of the string from left and from right composed just of + * contiguous characters found in 'cset', that is a null terminted C string. + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. + * + * Example: + * + * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); + * s = sdstrim(s,"Aa. :"); + * printf("%s\n", s); + * + * Output will be just "Hello World". + */ +sds sdstrim(sds s, const char *cset) { + char *start, *end, *sp, *ep; + size_t len; + + sp = start = s; + ep = end = s+sdslen(s)-1; + while(sp <= end && strchr(cset, *sp)) sp++; + while(ep > sp && strchr(cset, *ep)) ep--; + len = (sp > ep) ? 0 : ((ep-sp)+1); + if (s != sp) memmove(s, sp, len); + s[len] = '\0'; + sdssetlen(s,len); + return s; +} + +/* Turn the string into a smaller (or equal) string containing only the + * substring specified by the 'start' and 'end' indexes. + * + * start and end can be negative, where -1 means the last character of the + * string, -2 the penultimate character, and so forth. + * + * The interval is inclusive, so the start and end characters will be part + * of the resulting string. + * + * The string is modified in-place. + * + * Return value: + * -1 (error) if sdslen(s) is larger than maximum positive ssize_t value. + * 0 on success. + * + * Example: + * + * s = sdsnew("Hello World"); + * sdsrange(s,1,-1); => "ello World" + */ +int sdsrange(sds s, ssize_t start, ssize_t end) { + size_t newlen, len = sdslen(s); + if (len > SSIZE_MAX) return -1; + + if (len == 0) return 0; + if (start < 0) { + start = len+start; + if (start < 0) start = 0; + } + if (end < 0) { + end = len+end; + if (end < 0) end = 0; + } + newlen = (start > end) ? 0 : (end-start)+1; + if (newlen != 0) { + if (start >= (ssize_t)len) { + newlen = 0; + } else if (end >= (ssize_t)len) { + end = len-1; + newlen = (start > end) ? 0 : (end-start)+1; + } + } else { + start = 0; + } + if (start && newlen) memmove(s, s+start, newlen); + s[newlen] = 0; + sdssetlen(s,newlen); + return 0; +} + +/* Apply tolower() to every character of the sds string 's'. */ +void sdstolower(sds s) { + int len = sdslen(s), j; + + for (j = 0; j < len; j++) s[j] = tolower(s[j]); +} + +/* Apply toupper() to every character of the sds string 's'. */ +void sdstoupper(sds s) { + int len = sdslen(s), j; + + for (j = 0; j < len; j++) s[j] = toupper(s[j]); +} + +/* Compare two sds strings s1 and s2 with memcmp(). + * + * Return value: + * + * positive if s1 > s2. + * negative if s1 < s2. + * 0 if s1 and s2 are exactly the same binary string. + * + * If two strings share exactly the same prefix, but one of the two has + * additional characters, the longer string is considered to be greater than + * the smaller one. */ +int sdscmp(const sds s1, const sds s2) { + size_t l1, l2, minlen; + int cmp; + + l1 = sdslen(s1); + l2 = sdslen(s2); + minlen = (l1 < l2) ? l1 : l2; + cmp = memcmp(s1,s2,minlen); + if (cmp == 0) return l1-l2; + return cmp; +} + +/* Split 's' with separator in 'sep'. An array + * of sds strings is returned. *count will be set + * by reference to the number of tokens returned. + * + * On out of memory, zero length string, zero length + * separator, NULL is returned. + * + * Note that 'sep' is able to split a string using + * a multi-character separator. For example + * sdssplit("foo_-_bar","_-_"); will return two + * elements "foo" and "bar". + * + * This version of the function is binary-safe but + * requires length arguments. sdssplit() is just the + * same function but for zero-terminated strings. + */ +sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) { + int elements = 0, slots = 5, start = 0, j; + sds *tokens; + + if (seplen < 1 || len < 0) return NULL; + + tokens = s_malloc(sizeof(sds)*slots); + if (tokens == NULL) return NULL; + + if (len == 0) { + *count = 0; + return tokens; + } + for (j = 0; j < (len-(seplen-1)); j++) { + /* make sure there is room for the next element and the final one */ + if (slots < elements+2) { + sds *newtokens; + + slots *= 2; + newtokens = s_realloc(tokens,sizeof(sds)*slots); + if (newtokens == NULL) goto cleanup; + tokens = newtokens; + } + /* search the separator */ + if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { + tokens[elements] = sdsnewlen(s+start,j-start); + if (tokens[elements] == NULL) goto cleanup; + elements++; + start = j+seplen; + j = j+seplen-1; /* skip the separator */ + } + } + /* Add the final element. We are sure there is room in the tokens array. */ + tokens[elements] = sdsnewlen(s+start,len-start); + if (tokens[elements] == NULL) goto cleanup; + elements++; + *count = elements; + return tokens; + +cleanup: + { + int i; + for (i = 0; i < elements; i++) sdsfree(tokens[i]); + s_free(tokens); + *count = 0; + return NULL; + } +} + +/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */ +void sdsfreesplitres(sds *tokens, int count) { + if (!tokens) return; + while(count--) + sdsfree(tokens[count]); + s_free(tokens); +} + +/* Append to the sds string "s" an escaped string representation where + * all the non-printable characters (tested with isprint()) are turned into + * escapes in the form "\n\r\a...." or "\x". + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscatrepr(sds s, const char *p, size_t len) { + s = sdscatlen(s,"\"",1); + while(len--) { + switch(*p) { + case '\\': + case '"': + s = sdscatprintf(s,"\\%c",*p); + break; + case '\n': s = sdscatlen(s,"\\n",2); break; + case '\r': s = sdscatlen(s,"\\r",2); break; + case '\t': s = sdscatlen(s,"\\t",2); break; + case '\a': s = sdscatlen(s,"\\a",2); break; + case '\b': s = sdscatlen(s,"\\b",2); break; + default: + if (isprint(*p)) + s = sdscatprintf(s,"%c",*p); + else + s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); + break; + } + p++; + } + return sdscatlen(s,"\"",1); +} + +/* Helper function for sdssplitargs() that converts a hex digit into an + * integer from 0 to 15 */ +int hex_digit_to_int(char c) { + switch(c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': case 'A': return 10; + case 'b': case 'B': return 11; + case 'c': case 'C': return 12; + case 'd': case 'D': return 13; + case 'e': case 'E': return 14; + case 'f': case 'F': return 15; + default: return 0; + } +} + +/* Split a line into arguments, where every argument can be in the + * following programming-language REPL-alike form: + * + * foo bar "newline are supported\n" and "\xff\x00otherstuff" + * + * The number of arguments is stored into *argc, and an array + * of sds is returned. + * + * The caller should free the resulting array of sds strings with + * sdsfreesplitres(). + * + * Note that sdscatrepr() is able to convert back a string into + * a quoted string in the same format sdssplitargs() is able to parse. + * + * The function returns the allocated tokens on success, even when the + * input string is empty, or NULL if the input contains unbalanced + * quotes or closed quotes followed by non space characters + * as in: "foo"bar or "foo' + */ +sds *sdssplitargs(const char *line, int *argc) { + const char *p = line; + char *current = NULL; + char **vector = NULL; + + *argc = 0; + while(1) { + /* skip blanks */ + while(*p && isspace(*p)) p++; + if (*p) { + /* get a token */ + int inq=0; /* set to 1 if we are in "quotes" */ + int insq=0; /* set to 1 if we are in 'single quotes' */ + int done=0; + + if (current == NULL) current = sdsempty(); + while(!done) { + if (inq) { + if (*p == '\\' && *(p+1) == 'x' && + isxdigit(*(p+2)) && + isxdigit(*(p+3))) + { + unsigned char byte; + + byte = (hex_digit_to_int(*(p+2))*16)+ + hex_digit_to_int(*(p+3)); + current = sdscatlen(current,(char*)&byte,1); + p += 3; + } else if (*p == '\\' && *(p+1)) { + char c; + + p++; + switch(*p) { + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'b': c = '\b'; break; + case 'a': c = '\a'; break; + default: c = *p; break; + } + current = sdscatlen(current,&c,1); + } else if (*p == '"') { + /* closing quote must be followed by a space or + * nothing at all. */ + if (*(p+1) && !isspace(*(p+1))) goto err; + done=1; + } else if (!*p) { + /* unterminated quotes */ + goto err; + } else { + current = sdscatlen(current,p,1); + } + } else if (insq) { + if (*p == '\\' && *(p+1) == '\'') { + p++; + current = sdscatlen(current,"'",1); + } else if (*p == '\'') { + /* closing quote must be followed by a space or + * nothing at all. */ + if (*(p+1) && !isspace(*(p+1))) goto err; + done=1; + } else if (!*p) { + /* unterminated quotes */ + goto err; + } else { + current = sdscatlen(current,p,1); + } + } else { + switch(*p) { + case ' ': + case '\n': + case '\r': + case '\t': + case '\0': + done=1; + break; + case '"': + inq=1; + break; + case '\'': + insq=1; + break; + default: + current = sdscatlen(current,p,1); + break; + } + } + if (*p) p++; + } + /* add the token to the vector */ + { + char **new_vector = s_realloc(vector,((*argc)+1)*sizeof(char*)); + if (new_vector == NULL) { + s_free(vector); + return NULL; + } + + vector = new_vector; + vector[*argc] = current; + (*argc)++; + current = NULL; + } + } else { + /* Even on empty input string return something not NULL. */ + if (vector == NULL) vector = s_malloc(sizeof(void*)); + return vector; + } + } + +err: + while((*argc)--) + sdsfree(vector[*argc]); + s_free(vector); + if (current) sdsfree(current); + *argc = 0; + return NULL; +} + +/* Modify the string substituting all the occurrences of the set of + * characters specified in the 'from' string to the corresponding character + * in the 'to' array. + * + * For instance: sdsmapchars(mystring, "ho", "01", 2) + * will have the effect of turning the string "hello" into "0ell1". + * + * The function returns the sds string pointer, that is always the same + * as the input pointer since no resize is needed. */ +sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { + size_t j, i, l = sdslen(s); + + for (j = 0; j < l; j++) { + for (i = 0; i < setlen; i++) { + if (s[j] == from[i]) { + s[j] = to[i]; + break; + } + } + } + return s; +} + +/* Join an array of C strings using the specified separator (also a C string). + * Returns the result as an sds string. */ +sds sdsjoin(char **argv, int argc, char *sep) { + sds join = sdsempty(); + int j; + + for (j = 0; j < argc; j++) { + join = sdscat(join, argv[j]); + if (j != argc-1) join = sdscat(join,sep); + } + return join; +} + +/* Like sdsjoin, but joins an array of SDS strings. */ +sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) { + sds join = sdsempty(); + int j; + + for (j = 0; j < argc; j++) { + join = sdscatsds(join, argv[j]); + if (j != argc-1) join = sdscatlen(join,sep,seplen); + } + return join; +} + +/* Wrappers to the allocators used by SDS. Note that SDS will actually + * just use the macros defined into sdsalloc.h in order to avoid to pay + * the overhead of function calls. Here we define these wrappers only for + * the programs SDS is linked to, if they want to touch the SDS internals + * even if they use a different allocator. */ +void *sds_malloc(size_t size) { return s_malloc(size); } +void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); } +void sds_free(void *ptr) { s_free(ptr); } + +#if defined(SDS_TEST_MAIN) +#include +#include "testhelp.h" +#include "limits.h" + +#define UNUSED(x) (void)(x) +int sdsTest(void) { + { + sds x = sdsnew("foo"), y; + + test_cond("Create a string and obtain the length", + sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) + + sdsfree(x); + x = sdsnewlen("foo",2); + test_cond("Create a string with specified length", + sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) + + x = sdscat(x,"bar"); + test_cond("Strings concatenation", + sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); + + x = sdscpy(x,"a"); + test_cond("sdscpy() against an originally longer string", + sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) + + x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); + test_cond("sdscpy() against an originally shorter string", + sdslen(x) == 33 && + memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) + + sdsfree(x); + x = sdscatprintf(sdsempty(),"%d",123); + test_cond("sdscatprintf() seems working in the base case", + sdslen(x) == 3 && memcmp(x,"123\0",4) == 0) + + sdsfree(x); + x = sdsnew("--"); + x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX); + test_cond("sdscatfmt() seems working in the base case", + sdslen(x) == 60 && + memcmp(x,"--Hello Hi! World -9223372036854775808," + "9223372036854775807--",60) == 0) + printf("[%s]\n",x); + + sdsfree(x); + x = sdsnew("--"); + x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX); + test_cond("sdscatfmt() seems working with unsigned numbers", + sdslen(x) == 35 && + memcmp(x,"--4294967295,18446744073709551615--",35) == 0) + + sdsfree(x); + x = sdsnew(" x "); + sdstrim(x," x"); + test_cond("sdstrim() works when all chars match", + sdslen(x) == 0) + + sdsfree(x); + x = sdsnew(" x "); + sdstrim(x," "); + test_cond("sdstrim() works when a single char remains", + sdslen(x) == 1 && x[0] == 'x') + + sdsfree(x); + x = sdsnew("xxciaoyyy"); + sdstrim(x,"xy"); + test_cond("sdstrim() correctly trims characters", + sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) + + y = sdsdup(x); + sdsrange(y,1,1); + test_cond("sdsrange(...,1,1)", + sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) + + sdsfree(y); + y = sdsdup(x); + sdsrange(y,1,-1); + test_cond("sdsrange(...,1,-1)", + sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) + + sdsfree(y); + y = sdsdup(x); + sdsrange(y,-2,-1); + test_cond("sdsrange(...,-2,-1)", + sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) + + sdsfree(y); + y = sdsdup(x); + sdsrange(y,2,1); + test_cond("sdsrange(...,2,1)", + sdslen(y) == 0 && memcmp(y,"\0",1) == 0) + + sdsfree(y); + y = sdsdup(x); + sdsrange(y,1,100); + test_cond("sdsrange(...,1,100)", + sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) + + sdsfree(y); + y = sdsdup(x); + sdsrange(y,100,100); + test_cond("sdsrange(...,100,100)", + sdslen(y) == 0 && memcmp(y,"\0",1) == 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("foo"); + y = sdsnew("foa"); + test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("bar"); + y = sdsnew("bar"); + test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("aar"); + y = sdsnew("bar"); + test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) + + sdsfree(y); + sdsfree(x); + x = sdsnewlen("\a\n\0foo\r",7); + y = sdscatrepr(sdsempty(),x,sdslen(x)); + test_cond("sdscatrepr(...data...)", + memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0) + + { + unsigned int oldfree; + char *p; + int step = 10, j, i; + + sdsfree(x); + sdsfree(y); + x = sdsnew("0"); + test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0); + + /* Run the test a few times in order to hit the first two + * SDS header types. */ + for (i = 0; i < 10; i++) { + int oldlen = sdslen(x); + x = sdsMakeRoomFor(x,step); + int type = x[-1]&SDS_TYPE_MASK; + + test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen); + if (type != SDS_TYPE_5) { + test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step); + oldfree = sdsavail(x); + } + p = x+oldlen; + for (j = 0; j < step; j++) { + p[j] = 'A'+j; + } + sdsIncrLen(x,step); + } + test_cond("sdsMakeRoomFor() content", + memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0); + test_cond("sdsMakeRoomFor() final length",sdslen(x)==101); + + sdsfree(x); + } + } + test_report() + return 0; +} +#endif + +#ifdef SDS_TEST_MAIN +int main(void) { + return sdsTest(); +} +#endif diff --git a/ext/hiredis-1.0.2/sds.h b/ext/hiredis-1.0.2/sds.h new file mode 100644 index 000000000..eda8833b5 --- /dev/null +++ b/ext/hiredis-1.0.2/sds.h @@ -0,0 +1,278 @@ +/* SDSLib 2.0 -- A C dynamic strings library + * + * Copyright (c) 2006-2015, Salvatore Sanfilippo + * Copyright (c) 2015, Oran Agra + * Copyright (c) 2015, Redis Labs, Inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SDS_H +#define __SDS_H + +#define SDS_MAX_PREALLOC (1024*1024) +#ifdef _MSC_VER +#define __attribute__(x) +typedef long long ssize_t; +#define SSIZE_MAX (LLONG_MAX >> 1) +#endif + +#include +#include +#include + +typedef char *sds; + +/* Note: sdshdr5 is never used, we just access the flags byte directly. + * However is here to document the layout of type 5 SDS strings. */ +struct __attribute__ ((__packed__)) sdshdr5 { + unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr8 { + uint8_t len; /* used */ + uint8_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr16 { + uint16_t len; /* used */ + uint16_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr32 { + uint32_t len; /* used */ + uint32_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr64 { + uint64_t len; /* used */ + uint64_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; + +#define SDS_TYPE_5 0 +#define SDS_TYPE_8 1 +#define SDS_TYPE_16 2 +#define SDS_TYPE_32 3 +#define SDS_TYPE_64 4 +#define SDS_TYPE_MASK 7 +#define SDS_TYPE_BITS 3 +#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))); +#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) +#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) + +static inline size_t sdslen(const sds s) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + return SDS_TYPE_5_LEN(flags); + case SDS_TYPE_8: + return SDS_HDR(8,s)->len; + case SDS_TYPE_16: + return SDS_HDR(16,s)->len; + case SDS_TYPE_32: + return SDS_HDR(32,s)->len; + case SDS_TYPE_64: + return SDS_HDR(64,s)->len; + } + return 0; +} + +static inline size_t sdsavail(const sds s) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: { + return 0; + } + case SDS_TYPE_8: { + SDS_HDR_VAR(8,s); + return sh->alloc - sh->len; + } + case SDS_TYPE_16: { + SDS_HDR_VAR(16,s); + return sh->alloc - sh->len; + } + case SDS_TYPE_32: { + SDS_HDR_VAR(32,s); + return sh->alloc - sh->len; + } + case SDS_TYPE_64: { + SDS_HDR_VAR(64,s); + return sh->alloc - sh->len; + } + } + return 0; +} + +static inline void sdssetlen(sds s, size_t newlen) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + { + unsigned char *fp = ((unsigned char*)s)-1; + *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS)); + } + break; + case SDS_TYPE_8: + SDS_HDR(8,s)->len = (uint8_t)newlen; + break; + case SDS_TYPE_16: + SDS_HDR(16,s)->len = (uint16_t)newlen; + break; + case SDS_TYPE_32: + SDS_HDR(32,s)->len = (uint32_t)newlen; + break; + case SDS_TYPE_64: + SDS_HDR(64,s)->len = (uint64_t)newlen; + break; + } +} + +static inline void sdsinclen(sds s, size_t inc) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + { + unsigned char *fp = ((unsigned char*)s)-1; + unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc; + *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); + } + break; + case SDS_TYPE_8: + SDS_HDR(8,s)->len += (uint8_t)inc; + break; + case SDS_TYPE_16: + SDS_HDR(16,s)->len += (uint16_t)inc; + break; + case SDS_TYPE_32: + SDS_HDR(32,s)->len += (uint32_t)inc; + break; + case SDS_TYPE_64: + SDS_HDR(64,s)->len += (uint64_t)inc; + break; + } +} + +/* sdsalloc() = sdsavail() + sdslen() */ +static inline size_t sdsalloc(const sds s) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + return SDS_TYPE_5_LEN(flags); + case SDS_TYPE_8: + return SDS_HDR(8,s)->alloc; + case SDS_TYPE_16: + return SDS_HDR(16,s)->alloc; + case SDS_TYPE_32: + return SDS_HDR(32,s)->alloc; + case SDS_TYPE_64: + return SDS_HDR(64,s)->alloc; + } + return 0; +} + +static inline void sdssetalloc(sds s, size_t newlen) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + /* Nothing to do, this type has no total allocation info. */ + break; + case SDS_TYPE_8: + SDS_HDR(8,s)->alloc = (uint8_t)newlen; + break; + case SDS_TYPE_16: + SDS_HDR(16,s)->alloc = (uint16_t)newlen; + break; + case SDS_TYPE_32: + SDS_HDR(32,s)->alloc = (uint32_t)newlen; + break; + case SDS_TYPE_64: + SDS_HDR(64,s)->alloc = (uint64_t)newlen; + break; + } +} + +sds sdsnewlen(const void *init, size_t initlen); +sds sdsnew(const char *init); +sds sdsempty(void); +sds sdsdup(const sds s); +void sdsfree(sds s); +sds sdsgrowzero(sds s, size_t len); +sds sdscatlen(sds s, const void *t, size_t len); +sds sdscat(sds s, const char *t); +sds sdscatsds(sds s, const sds t); +sds sdscpylen(sds s, const char *t, size_t len); +sds sdscpy(sds s, const char *t); + +sds sdscatvprintf(sds s, const char *fmt, va_list ap); +#ifdef __GNUC__ +sds sdscatprintf(sds s, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#else +sds sdscatprintf(sds s, const char *fmt, ...); +#endif + +sds sdscatfmt(sds s, char const *fmt, ...); +sds sdstrim(sds s, const char *cset); +int sdsrange(sds s, ssize_t start, ssize_t end); +void sdsupdatelen(sds s); +void sdsclear(sds s); +int sdscmp(const sds s1, const sds s2); +sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); +void sdsfreesplitres(sds *tokens, int count); +void sdstolower(sds s); +void sdstoupper(sds s); +sds sdsfromlonglong(long long value); +sds sdscatrepr(sds s, const char *p, size_t len); +sds *sdssplitargs(const char *line, int *argc); +sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); +sds sdsjoin(char **argv, int argc, char *sep); +sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); + +/* Low level functions exposed to the user API */ +sds sdsMakeRoomFor(sds s, size_t addlen); +void sdsIncrLen(sds s, int incr); +sds sdsRemoveFreeSpace(sds s); +size_t sdsAllocSize(sds s); +void *sdsAllocPtr(sds s); + +/* Export the allocator used by SDS to the program using SDS. + * Sometimes the program SDS is linked to, may use a different set of + * allocators, but may want to allocate or free things that SDS will + * respectively free or allocate. */ +void *sds_malloc(size_t size); +void *sds_realloc(void *ptr, size_t size); +void sds_free(void *ptr); + +#ifdef REDIS_TEST +int sdsTest(int argc, char *argv[]); +#endif + +#endif diff --git a/ext/hiredis-1.0.2/sdsalloc.h b/ext/hiredis-1.0.2/sdsalloc.h new file mode 100644 index 000000000..5538dd94c --- /dev/null +++ b/ext/hiredis-1.0.2/sdsalloc.h @@ -0,0 +1,44 @@ +/* SDSLib 2.0 -- A C dynamic strings library + * + * Copyright (c) 2006-2015, Salvatore Sanfilippo + * Copyright (c) 2015, Oran Agra + * Copyright (c) 2015, Redis Labs, Inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* SDS allocator selection. + * + * This file is used in order to change the SDS allocator at compile time. + * Just define the following defines to what you want to use. Also add + * the include of your alternate allocator if needed (not needed in order + * to use the default libc allocator). */ + +#include "alloc.h" + +#define s_malloc hi_malloc +#define s_realloc hi_realloc +#define s_free hi_free diff --git a/ext/hiredis-1.0.2/sockcompat.c b/ext/hiredis-1.0.2/sockcompat.c new file mode 100644 index 000000000..f99d14b05 --- /dev/null +++ b/ext/hiredis-1.0.2/sockcompat.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2019, Marcus Geelnard + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define REDIS_SOCKCOMPAT_IMPLEMENTATION +#include "sockcompat.h" + +#ifdef _WIN32 +static int _wsaErrorToErrno(int err) { + switch (err) { + case WSAEWOULDBLOCK: + return EWOULDBLOCK; + case WSAEINPROGRESS: + return EINPROGRESS; + case WSAEALREADY: + return EALREADY; + case WSAENOTSOCK: + return ENOTSOCK; + case WSAEDESTADDRREQ: + return EDESTADDRREQ; + case WSAEMSGSIZE: + return EMSGSIZE; + case WSAEPROTOTYPE: + return EPROTOTYPE; + case WSAENOPROTOOPT: + return ENOPROTOOPT; + case WSAEPROTONOSUPPORT: + return EPROTONOSUPPORT; + case WSAEOPNOTSUPP: + return EOPNOTSUPP; + case WSAEAFNOSUPPORT: + return EAFNOSUPPORT; + case WSAEADDRINUSE: + return EADDRINUSE; + case WSAEADDRNOTAVAIL: + return EADDRNOTAVAIL; + case WSAENETDOWN: + return ENETDOWN; + case WSAENETUNREACH: + return ENETUNREACH; + case WSAENETRESET: + return ENETRESET; + case WSAECONNABORTED: + return ECONNABORTED; + case WSAECONNRESET: + return ECONNRESET; + case WSAENOBUFS: + return ENOBUFS; + case WSAEISCONN: + return EISCONN; + case WSAENOTCONN: + return ENOTCONN; + case WSAETIMEDOUT: + return ETIMEDOUT; + case WSAECONNREFUSED: + return ECONNREFUSED; + case WSAELOOP: + return ELOOP; + case WSAENAMETOOLONG: + return ENAMETOOLONG; + case WSAEHOSTUNREACH: + return EHOSTUNREACH; + case WSAENOTEMPTY: + return ENOTEMPTY; + default: + /* We just return a generic I/O error if we could not find a relevant error. */ + return EIO; + } +} + +static void _updateErrno(int success) { + errno = success ? 0 : _wsaErrorToErrno(WSAGetLastError()); +} + +static int _initWinsock() { + static int s_initialized = 0; + if (!s_initialized) { + static WSADATA wsadata; + int err = WSAStartup(MAKEWORD(2,2), &wsadata); + if (err != 0) { + errno = _wsaErrorToErrno(err); + return 0; + } + s_initialized = 1; + } + return 1; +} + +int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { + /* Note: This function is likely to be called before other functions, so run init here. */ + if (!_initWinsock()) { + return EAI_FAIL; + } + + switch (getaddrinfo(node, service, hints, res)) { + case 0: return 0; + case WSATRY_AGAIN: return EAI_AGAIN; + case WSAEINVAL: return EAI_BADFLAGS; + case WSAEAFNOSUPPORT: return EAI_FAMILY; + case WSA_NOT_ENOUGH_MEMORY: return EAI_MEMORY; + case WSAHOST_NOT_FOUND: return EAI_NONAME; + case WSATYPE_NOT_FOUND: return EAI_SERVICE; + case WSAESOCKTNOSUPPORT: return EAI_SOCKTYPE; + default: return EAI_FAIL; /* Including WSANO_RECOVERY */ + } +} + +const char *win32_gai_strerror(int errcode) { + switch (errcode) { + case 0: errcode = 0; break; + case EAI_AGAIN: errcode = WSATRY_AGAIN; break; + case EAI_BADFLAGS: errcode = WSAEINVAL; break; + case EAI_FAMILY: errcode = WSAEAFNOSUPPORT; break; + case EAI_MEMORY: errcode = WSA_NOT_ENOUGH_MEMORY; break; + case EAI_NONAME: errcode = WSAHOST_NOT_FOUND; break; + case EAI_SERVICE: errcode = WSATYPE_NOT_FOUND; break; + case EAI_SOCKTYPE: errcode = WSAESOCKTNOSUPPORT; break; + default: errcode = WSANO_RECOVERY; break; /* Including EAI_FAIL */ + } + return gai_strerror(errcode); +} + +void win32_freeaddrinfo(struct addrinfo *res) { + freeaddrinfo(res); +} + +SOCKET win32_socket(int domain, int type, int protocol) { + SOCKET s; + + /* Note: This function is likely to be called before other functions, so run init here. */ + if (!_initWinsock()) { + return INVALID_SOCKET; + } + + _updateErrno((s = socket(domain, type, protocol)) != INVALID_SOCKET); + return s; +} + +int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp) { + int ret = ioctlsocket(fd, (long)request, argp); + _updateErrno(ret != SOCKET_ERROR); + return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) { + int ret = bind(sockfd, addr, addrlen); + _updateErrno(ret != SOCKET_ERROR); + return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) { + int ret = connect(sockfd, addr, addrlen); + _updateErrno(ret != SOCKET_ERROR); + + /* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as + * EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX + * logic consistent. */ + if (errno == EWOULDBLOCK) { + errno = EINPROGRESS; + } + + return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen) { + int ret = 0; + if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) { + if (*optlen >= sizeof (struct timeval)) { + struct timeval *tv = optval; + DWORD timeout = 0; + socklen_t dwlen = 0; + ret = getsockopt(sockfd, level, optname, (char *)&timeout, &dwlen); + tv->tv_sec = timeout / 1000; + tv->tv_usec = (timeout * 1000) % 1000000; + } else { + ret = WSAEFAULT; + } + *optlen = sizeof (struct timeval); + } else { + ret = getsockopt(sockfd, level, optname, (char*)optval, optlen); + } + _updateErrno(ret != SOCKET_ERROR); + return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) { + int ret = 0; + if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) { + const struct timeval *tv = optval; + DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000; + ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD)); + } else { + ret = setsockopt(sockfd, level, optname, (const char*)optval, optlen); + } + _updateErrno(ret != SOCKET_ERROR); + return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_close(SOCKET fd) { + int ret = closesocket(fd); + _updateErrno(ret != SOCKET_ERROR); + return ret != SOCKET_ERROR ? ret : -1; +} + +ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags) { + int ret = recv(sockfd, (char*)buf, (int)len, flags); + _updateErrno(ret != SOCKET_ERROR); + return ret != SOCKET_ERROR ? ret : -1; +} + +ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags) { + int ret = send(sockfd, (const char*)buf, (int)len, flags); + _updateErrno(ret != SOCKET_ERROR); + return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) { + int ret = WSAPoll(fds, nfds, timeout); + _updateErrno(ret != SOCKET_ERROR); + return ret != SOCKET_ERROR ? ret : -1; +} +#endif /* _WIN32 */ diff --git a/ext/hiredis-1.0.2/sockcompat.h b/ext/hiredis-1.0.2/sockcompat.h new file mode 100644 index 000000000..85810e848 --- /dev/null +++ b/ext/hiredis-1.0.2/sockcompat.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019, Marcus Geelnard + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SOCKCOMPAT_H +#define __SOCKCOMPAT_H + +#ifndef _WIN32 +/* For POSIX systems we use the standard BSD socket API. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +/* For Windows we use winsock. */ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */ +#include +#include +#include +#include + +#ifdef _MSC_VER +typedef long long ssize_t; +#endif + +/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */ +int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); +const char *win32_gai_strerror(int errcode); +void win32_freeaddrinfo(struct addrinfo *res); +SOCKET win32_socket(int domain, int type, int protocol); +int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp); +int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); +int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); +int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen); +int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen); +int win32_close(SOCKET fd); +ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags); +ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags); +typedef ULONG nfds_t; +int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout); + +#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION +#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res) +#undef gai_strerror +#define gai_strerror(errcode) win32_gai_strerror(errcode) +#define freeaddrinfo(res) win32_freeaddrinfo(res) +#define socket(domain, type, protocol) win32_socket(domain, type, protocol) +#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp) +#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen) +#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen) +#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen) +#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen) +#define close(fd) win32_close(fd) +#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags) +#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags) +#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout) +#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */ +#endif /* _WIN32 */ + +#endif /* __SOCKCOMPAT_H */ diff --git a/ext/hiredis-1.0.2/ssl.c b/ext/hiredis-1.0.2/ssl.c new file mode 100644 index 000000000..7df58fbde --- /dev/null +++ b/ext/hiredis-1.0.2/ssl.c @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * Copyright (c) 2019, Redis Labs + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hiredis.h" +#include "async.h" + +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif + +#include +#include + +#include "win32.h" +#include "async_private.h" +#include "hiredis_ssl.h" + +void __redisSetError(redisContext *c, int type, const char *str); + +struct redisSSLContext { + /* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */ + SSL_CTX *ssl_ctx; + + /* Requested SNI, or NULL */ + char *server_name; +}; + +/* The SSL connection context is attached to SSL/TLS connections as a privdata. */ +typedef struct redisSSL { + /** + * OpenSSL SSL object. + */ + SSL *ssl; + + /** + * SSL_write() requires to be called again with the same arguments it was + * previously called with in the event of an SSL_read/SSL_write situation + */ + size_t lastLen; + + /** Whether the SSL layer requires read (possibly before a write) */ + int wantRead; + + /** + * Whether a write was requested prior to a read. If set, the write() + * should resume whenever a read takes place, if possible + */ + int pendingWrite; +} redisSSL; + +/* Forward declaration */ +redisContextFuncs redisContextSSLFuncs; + +/** + * OpenSSL global initialization and locking handling callbacks. + * Note that this is only required for OpenSSL < 1.1.0. + */ + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#define HIREDIS_USE_CRYPTO_LOCKS +#endif + +#ifdef HIREDIS_USE_CRYPTO_LOCKS +#ifdef _WIN32 +typedef CRITICAL_SECTION sslLockType; +static void sslLockInit(sslLockType* l) { + InitializeCriticalSection(l); +} +static void sslLockAcquire(sslLockType* l) { + EnterCriticalSection(l); +} +static void sslLockRelease(sslLockType* l) { + LeaveCriticalSection(l); +} +#else +typedef pthread_mutex_t sslLockType; +static void sslLockInit(sslLockType *l) { + pthread_mutex_init(l, NULL); +} +static void sslLockAcquire(sslLockType *l) { + pthread_mutex_lock(l); +} +static void sslLockRelease(sslLockType *l) { + pthread_mutex_unlock(l); +} +#endif + +static sslLockType* ossl_locks; + +static void opensslDoLock(int mode, int lkid, const char *f, int line) { + sslLockType *l = ossl_locks + lkid; + + if (mode & CRYPTO_LOCK) { + sslLockAcquire(l); + } else { + sslLockRelease(l); + } + + (void)f; + (void)line; +} + +static int initOpensslLocks(void) { + unsigned ii, nlocks; + if (CRYPTO_get_locking_callback() != NULL) { + /* Someone already set the callback before us. Don't destroy it! */ + return REDIS_OK; + } + nlocks = CRYPTO_num_locks(); + ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks); + if (ossl_locks == NULL) + return REDIS_ERR; + + for (ii = 0; ii < nlocks; ii++) { + sslLockInit(ossl_locks + ii); + } + CRYPTO_set_locking_callback(opensslDoLock); + return REDIS_OK; +} +#endif /* HIREDIS_USE_CRYPTO_LOCKS */ + +int redisInitOpenSSL(void) +{ + SSL_library_init(); +#ifdef HIREDIS_USE_CRYPTO_LOCKS + initOpensslLocks(); +#endif + + return REDIS_OK; +} + +/** + * redisSSLContext helper context destruction. + */ + +const char *redisSSLContextGetError(redisSSLContextError error) +{ + switch (error) { + case REDIS_SSL_CTX_NONE: + return "No Error"; + case REDIS_SSL_CTX_CREATE_FAILED: + return "Failed to create OpenSSL SSL_CTX"; + case REDIS_SSL_CTX_CERT_KEY_REQUIRED: + return "Client cert and key must both be specified or skipped"; + case REDIS_SSL_CTX_CA_CERT_LOAD_FAILED: + return "Failed to load CA Certificate or CA Path"; + case REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED: + return "Failed to load client certificate"; + case REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED: + return "Failed to load private key"; + default: + return "Unknown error code"; + } +} + +void redisFreeSSLContext(redisSSLContext *ctx) +{ + if (!ctx) + return; + + if (ctx->server_name) { + hi_free(ctx->server_name); + ctx->server_name = NULL; + } + + if (ctx->ssl_ctx) { + SSL_CTX_free(ctx->ssl_ctx); + ctx->ssl_ctx = NULL; + } + + hi_free(ctx); +} + + +/** + * redisSSLContext helper context initialization. + */ + +redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath, + const char *cert_filename, const char *private_key_filename, + const char *server_name, redisSSLContextError *error) +{ + redisSSLContext *ctx = hi_calloc(1, sizeof(redisSSLContext)); + if (ctx == NULL) + goto error; + + ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ctx->ssl_ctx) { + if (error) *error = REDIS_SSL_CTX_CREATE_FAILED; + goto error; + } + + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); + + if ((cert_filename != NULL && private_key_filename == NULL) || + (private_key_filename != NULL && cert_filename == NULL)) { + if (error) *error = REDIS_SSL_CTX_CERT_KEY_REQUIRED; + goto error; + } + + if (capath || cacert_filename) { + if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, cacert_filename, capath)) { + if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED; + goto error; + } + } + + if (cert_filename) { + if (!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, cert_filename)) { + if (error) *error = REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED; + goto error; + } + if (!SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, private_key_filename, SSL_FILETYPE_PEM)) { + if (error) *error = REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED; + goto error; + } + } + + if (server_name) + ctx->server_name = hi_strdup(server_name); + + return ctx; + +error: + redisFreeSSLContext(ctx); + return NULL; +} + +/** + * SSL Connection initialization. + */ + + +static int redisSSLConnect(redisContext *c, SSL *ssl) { + if (c->privctx) { + __redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated"); + return REDIS_ERR; + } + + redisSSL *rssl = hi_calloc(1, sizeof(redisSSL)); + if (rssl == NULL) { + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; + } + + c->funcs = &redisContextSSLFuncs; + rssl->ssl = ssl; + + SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_set_fd(rssl->ssl, c->fd); + SSL_set_connect_state(rssl->ssl); + + ERR_clear_error(); + int rv = SSL_connect(rssl->ssl); + if (rv == 1) { + c->privctx = rssl; + return REDIS_OK; + } + + rv = SSL_get_error(rssl->ssl, rv); + if (((c->flags & REDIS_BLOCK) == 0) && + (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) { + c->privctx = rssl; + return REDIS_OK; + } + + if (c->err == 0) { + char err[512]; + if (rv == SSL_ERROR_SYSCALL) + snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",strerror(errno)); + else { + unsigned long e = ERR_peek_last_error(); + snprintf(err,sizeof(err)-1,"SSL_connect failed: %s", + ERR_reason_error_string(e)); + } + __redisSetError(c, REDIS_ERR_IO, err); + } + + hi_free(rssl); + return REDIS_ERR; +} + +/** + * A wrapper around redisSSLConnect() for users who manage their own context and + * create their own SSL object. + */ + +int redisInitiateSSL(redisContext *c, SSL *ssl) { + return redisSSLConnect(c, ssl); +} + +/** + * A wrapper around redisSSLConnect() for users who use redisSSLContext and don't + * manage their own SSL objects. + */ + +int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx) +{ + if (!c || !redis_ssl_ctx) + return REDIS_ERR; + + /* We want to verify that redisSSLConnect() won't fail on this, as it will + * not own the SSL object in that case and we'll end up leaking. + */ + if (c->privctx) + return REDIS_ERR; + + SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx); + if (!ssl) { + __redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance"); + goto error; + } + + if (redis_ssl_ctx->server_name) { + if (!SSL_set_tlsext_host_name(ssl, redis_ssl_ctx->server_name)) { + __redisSetError(c, REDIS_ERR_OTHER, "Failed to set server_name/SNI"); + goto error; + } + } + + return redisSSLConnect(c, ssl); + +error: + if (ssl) + SSL_free(ssl); + return REDIS_ERR; +} + +static int maybeCheckWant(redisSSL *rssl, int rv) { + /** + * If the error is WANT_READ or WANT_WRITE, the appropriate flags are set + * and true is returned. False is returned otherwise + */ + if (rv == SSL_ERROR_WANT_READ) { + rssl->wantRead = 1; + return 1; + } else if (rv == SSL_ERROR_WANT_WRITE) { + rssl->pendingWrite = 1; + return 1; + } else { + return 0; + } +} + +/** + * Implementation of redisContextFuncs for SSL connections. + */ + +static void redisSSLFree(void *privctx){ + redisSSL *rsc = privctx; + + if (!rsc) return; + if (rsc->ssl) { + SSL_free(rsc->ssl); + rsc->ssl = NULL; + } + hi_free(rsc); +} + +static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) { + redisSSL *rssl = c->privctx; + + int nread = SSL_read(rssl->ssl, buf, bufcap); + if (nread > 0) { + return nread; + } else if (nread == 0) { + __redisSetError(c, REDIS_ERR_EOF, "Server closed the connection"); + return -1; + } else { + int err = SSL_get_error(rssl->ssl, nread); + if (c->flags & REDIS_BLOCK) { + /** + * In blocking mode, we should never end up in a situation where + * we get an error without it being an actual error, except + * in the case of EINTR, which can be spuriously received from + * debuggers or whatever. + */ + if (errno == EINTR) { + return 0; + } else { + const char *msg = NULL; + if (errno == EAGAIN) { + msg = "Resource temporarily unavailable"; + } + __redisSetError(c, REDIS_ERR_IO, msg); + return -1; + } + } + + /** + * We can very well get an EWOULDBLOCK/EAGAIN, however + */ + if (maybeCheckWant(rssl, err)) { + return 0; + } else { + __redisSetError(c, REDIS_ERR_IO, NULL); + return -1; + } + } +} + +static ssize_t redisSSLWrite(redisContext *c) { + redisSSL *rssl = c->privctx; + + size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf); + int rv = SSL_write(rssl->ssl, c->obuf, len); + + if (rv > 0) { + rssl->lastLen = 0; + } else if (rv < 0) { + rssl->lastLen = len; + + int err = SSL_get_error(rssl->ssl, rv); + if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(rssl, err)) { + return 0; + } else { + __redisSetError(c, REDIS_ERR_IO, NULL); + return -1; + } + } + return rv; +} + +static void redisSSLAsyncRead(redisAsyncContext *ac) { + int rv; + redisSSL *rssl = ac->c.privctx; + redisContext *c = &ac->c; + + rssl->wantRead = 0; + + if (rssl->pendingWrite) { + int done; + + /* This is probably just a write event */ + rssl->pendingWrite = 0; + rv = redisBufferWrite(c, &done); + if (rv == REDIS_ERR) { + __redisAsyncDisconnect(ac); + return; + } else if (!done) { + _EL_ADD_WRITE(ac); + } + } + + rv = redisBufferRead(c); + if (rv == REDIS_ERR) { + __redisAsyncDisconnect(ac); + } else { + _EL_ADD_READ(ac); + redisProcessCallbacks(ac); + } +} + +static void redisSSLAsyncWrite(redisAsyncContext *ac) { + int rv, done = 0; + redisSSL *rssl = ac->c.privctx; + redisContext *c = &ac->c; + + rssl->pendingWrite = 0; + rv = redisBufferWrite(c, &done); + if (rv == REDIS_ERR) { + __redisAsyncDisconnect(ac); + return; + } + + if (!done) { + if (rssl->wantRead) { + /* Need to read-before-write */ + rssl->pendingWrite = 1; + _EL_DEL_WRITE(ac); + } else { + /* No extra reads needed, just need to write more */ + _EL_ADD_WRITE(ac); + } + } else { + /* Already done! */ + _EL_DEL_WRITE(ac); + } + + /* Always reschedule a read */ + _EL_ADD_READ(ac); +} + +redisContextFuncs redisContextSSLFuncs = { + .free_privctx = redisSSLFree, + .async_read = redisSSLAsyncRead, + .async_write = redisSSLAsyncWrite, + .read = redisSSLRead, + .write = redisSSLWrite +}; + diff --git a/ext/hiredis-1.0.2/test.c b/ext/hiredis-1.0.2/test.c new file mode 100644 index 000000000..397f5640e --- /dev/null +++ b/ext/hiredis-1.0.2/test.c @@ -0,0 +1,1401 @@ +#include "fmacros.h" +#include "sockcompat.h" +#include +#include +#include +#ifndef _WIN32 +#include +#include +#endif +#include +#include +#include +#include + +#include "hiredis.h" +#include "async.h" +#ifdef HIREDIS_TEST_SSL +#include "hiredis_ssl.h" +#endif +#include "net.h" +#include "win32.h" + +enum connection_type { + CONN_TCP, + CONN_UNIX, + CONN_FD, + CONN_SSL +}; + +struct config { + enum connection_type type; + + struct { + const char *host; + int port; + struct timeval timeout; + } tcp; + + struct { + const char *path; + } unix_sock; + + struct { + const char *host; + int port; + const char *ca_cert; + const char *cert; + const char *key; + } ssl; +}; + +struct privdata { + int dtor_counter; +}; + +#ifdef HIREDIS_TEST_SSL +redisSSLContext *_ssl_ctx = NULL; +#endif + +/* The following lines make up our testing "framework" :) */ +static int tests = 0, fails = 0, skips = 0; +#define test(_s) { printf("#%02d ", ++tests); printf(_s); } +#define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;} +#define test_skipped() { printf("\033[01;33mSKIPPED\033[0;0m\n"); skips++; } + +static long long usec(void) { +#ifndef _MSC_VER + struct timeval tv; + gettimeofday(&tv,NULL); + return (((long long)tv.tv_sec)*1000000)+tv.tv_usec; +#else + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + return (((long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime) / 10; +#endif +} + +/* The assert() calls below have side effects, so we need assert() + * even if we are compiling without asserts (-DNDEBUG). */ +#ifdef NDEBUG +#undef assert +#define assert(e) (void)(e) +#endif + +/* Helper to extract Redis version information. Aborts on any failure. */ +#define REDIS_VERSION_FIELD "redis_version:" +void get_redis_version(redisContext *c, int *majorptr, int *minorptr) { + redisReply *reply; + char *eptr, *s, *e; + int major, minor; + + reply = redisCommand(c, "INFO"); + if (reply == NULL || c->err || reply->type != REDIS_REPLY_STRING) + goto abort; + if ((s = strstr(reply->str, REDIS_VERSION_FIELD)) == NULL) + goto abort; + + s += strlen(REDIS_VERSION_FIELD); + + /* We need a field terminator and at least 'x.y.z' (5) bytes of data */ + if ((e = strstr(s, "\r\n")) == NULL || (e - s) < 5) + goto abort; + + /* Extract version info */ + major = strtol(s, &eptr, 10); + if (*eptr != '.') goto abort; + minor = strtol(eptr+1, NULL, 10); + + /* Push info the caller wants */ + if (majorptr) *majorptr = major; + if (minorptr) *minorptr = minor; + + freeReplyObject(reply); + return; + +abort: + freeReplyObject(reply); + fprintf(stderr, "Error: Cannot determine Redis version, aborting\n"); + exit(1); +} + +static redisContext *select_database(redisContext *c) { + redisReply *reply; + + /* Switch to DB 9 for testing, now that we know we can chat. */ + reply = redisCommand(c,"SELECT 9"); + assert(reply != NULL); + freeReplyObject(reply); + + /* Make sure the DB is emtpy */ + reply = redisCommand(c,"DBSIZE"); + assert(reply != NULL); + if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) { + /* Awesome, DB 9 is empty and we can continue. */ + freeReplyObject(reply); + } else { + printf("Database #9 is not empty, test can not continue\n"); + exit(1); + } + + return c; +} + +/* Switch protocol */ +static void send_hello(redisContext *c, int version) { + redisReply *reply; + int expected; + + reply = redisCommand(c, "HELLO %d", version); + expected = version == 3 ? REDIS_REPLY_MAP : REDIS_REPLY_ARRAY; + assert(reply != NULL && reply->type == expected); + freeReplyObject(reply); +} + +/* Togggle client tracking */ +static void send_client_tracking(redisContext *c, const char *str) { + redisReply *reply; + + reply = redisCommand(c, "CLIENT TRACKING %s", str); + assert(reply != NULL && reply->type == REDIS_REPLY_STATUS); + freeReplyObject(reply); +} + +static int disconnect(redisContext *c, int keep_fd) { + redisReply *reply; + + /* Make sure we're on DB 9. */ + reply = redisCommand(c,"SELECT 9"); + assert(reply != NULL); + freeReplyObject(reply); + reply = redisCommand(c,"FLUSHDB"); + assert(reply != NULL); + freeReplyObject(reply); + + /* Free the context as well, but keep the fd if requested. */ + if (keep_fd) + return redisFreeKeepFd(c); + redisFree(c); + return -1; +} + +static void do_ssl_handshake(redisContext *c) { +#ifdef HIREDIS_TEST_SSL + redisInitiateSSLWithContext(c, _ssl_ctx); + if (c->err) { + printf("SSL error: %s\n", c->errstr); + redisFree(c); + exit(1); + } +#else + (void) c; +#endif +} + +static redisContext *do_connect(struct config config) { + redisContext *c = NULL; + + if (config.type == CONN_TCP) { + c = redisConnect(config.tcp.host, config.tcp.port); + } else if (config.type == CONN_SSL) { + c = redisConnect(config.ssl.host, config.ssl.port); + } else if (config.type == CONN_UNIX) { + c = redisConnectUnix(config.unix_sock.path); + } else if (config.type == CONN_FD) { + /* Create a dummy connection just to get an fd to inherit */ + redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path); + if (dummy_ctx) { + int fd = disconnect(dummy_ctx, 1); + printf("Connecting to inherited fd %d\n", fd); + c = redisConnectFd(fd); + } + } else { + assert(NULL); + } + + if (c == NULL) { + printf("Connection error: can't allocate redis context\n"); + exit(1); + } else if (c->err) { + printf("Connection error: %s\n", c->errstr); + redisFree(c); + exit(1); + } + + if (config.type == CONN_SSL) { + do_ssl_handshake(c); + } + + return select_database(c); +} + +static void do_reconnect(redisContext *c, struct config config) { + redisReconnect(c); + + if (config.type == CONN_SSL) { + do_ssl_handshake(c); + } +} + +static void test_format_commands(void) { + char *cmd; + int len; + + test("Format command without interpolation: "); + len = redisFormatCommand(&cmd,"SET foo bar"); + test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && + len == 4+4+(3+2)+4+(3+2)+4+(3+2)); + hi_free(cmd); + + test("Format command with %%s string interpolation: "); + len = redisFormatCommand(&cmd,"SET %s %s","foo","bar"); + test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && + len == 4+4+(3+2)+4+(3+2)+4+(3+2)); + hi_free(cmd); + + test("Format command with %%s and an empty string: "); + len = redisFormatCommand(&cmd,"SET %s %s","foo",""); + test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && + len == 4+4+(3+2)+4+(3+2)+4+(0+2)); + hi_free(cmd); + + test("Format command with an empty string in between proper interpolations: "); + len = redisFormatCommand(&cmd,"SET %s %s","","foo"); + test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 && + len == 4+4+(3+2)+4+(0+2)+4+(3+2)); + hi_free(cmd); + + test("Format command with %%b string interpolation: "); + len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"b\0r",(size_t)3); + test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 && + len == 4+4+(3+2)+4+(3+2)+4+(3+2)); + hi_free(cmd); + + test("Format command with %%b and an empty string: "); + len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"",(size_t)0); + test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && + len == 4+4+(3+2)+4+(3+2)+4+(0+2)); + hi_free(cmd); + + test("Format command with literal %%: "); + len = redisFormatCommand(&cmd,"SET %% %%"); + test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 && + len == 4+4+(3+2)+4+(1+2)+4+(1+2)); + hi_free(cmd); + + /* Vararg width depends on the type. These tests make sure that the + * width is correctly determined using the format and subsequent varargs + * can correctly be interpolated. */ +#define INTEGER_WIDTH_TEST(fmt, type) do { \ + type value = 123; \ + test("Format command with printf-delegation (" #type "): "); \ + len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \ + test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \ + len == 4+5+(12+2)+4+(9+2)); \ + hi_free(cmd); \ +} while(0) + +#define FLOAT_WIDTH_TEST(type) do { \ + type value = 123.0; \ + test("Format command with printf-delegation (" #type "): "); \ + len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \ + test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \ + len == 4+5+(12+2)+4+(9+2)); \ + hi_free(cmd); \ +} while(0) + + INTEGER_WIDTH_TEST("d", int); + INTEGER_WIDTH_TEST("hhd", char); + INTEGER_WIDTH_TEST("hd", short); + INTEGER_WIDTH_TEST("ld", long); + INTEGER_WIDTH_TEST("lld", long long); + INTEGER_WIDTH_TEST("u", unsigned int); + INTEGER_WIDTH_TEST("hhu", unsigned char); + INTEGER_WIDTH_TEST("hu", unsigned short); + INTEGER_WIDTH_TEST("lu", unsigned long); + INTEGER_WIDTH_TEST("llu", unsigned long long); + FLOAT_WIDTH_TEST(float); + FLOAT_WIDTH_TEST(double); + + test("Format command with invalid printf format: "); + len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",(size_t)3); + test_cond(len == -1); + + const char *argv[3]; + argv[0] = "SET"; + argv[1] = "foo\0xxx"; + argv[2] = "bar"; + size_t lens[3] = { 3, 7, 3 }; + int argc = 3; + + test("Format command by passing argc/argv without lengths: "); + len = redisFormatCommandArgv(&cmd,argc,argv,NULL); + test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && + len == 4+4+(3+2)+4+(3+2)+4+(3+2)); + hi_free(cmd); + + test("Format command by passing argc/argv with lengths: "); + len = redisFormatCommandArgv(&cmd,argc,argv,lens); + test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && + len == 4+4+(3+2)+4+(7+2)+4+(3+2)); + hi_free(cmd); + + sds sds_cmd; + + sds_cmd = NULL; + test("Format command into sds by passing argc/argv without lengths: "); + len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL); + test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && + len == 4+4+(3+2)+4+(3+2)+4+(3+2)); + sdsfree(sds_cmd); + + sds_cmd = NULL; + test("Format command into sds by passing argc/argv with lengths: "); + len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens); + test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && + len == 4+4+(3+2)+4+(7+2)+4+(3+2)); + sdsfree(sds_cmd); +} + +static void test_append_formatted_commands(struct config config) { + redisContext *c; + redisReply *reply; + char *cmd; + int len; + + c = do_connect(config); + + test("Append format command: "); + + len = redisFormatCommand(&cmd, "SET foo bar"); + + test_cond(redisAppendFormattedCommand(c, cmd, len) == REDIS_OK); + + assert(redisGetReply(c, (void*)&reply) == REDIS_OK); + + hi_free(cmd); + freeReplyObject(reply); + + disconnect(c, 0); +} + +static void test_reply_reader(void) { + redisReader *reader; + void *reply, *root; + int ret; + int i; + + test("Error handling in reply parser: "); + reader = redisReaderCreate(); + redisReaderFeed(reader,(char*)"@foo\r\n",6); + ret = redisReaderGetReply(reader,NULL); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0); + redisReaderFree(reader); + + /* when the reply already contains multiple items, they must be free'd + * on an error. valgrind will bark when this doesn't happen. */ + test("Memory cleanup in reply parser: "); + reader = redisReaderCreate(); + redisReaderFeed(reader,(char*)"*2\r\n",4); + redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11); + redisReaderFeed(reader,(char*)"@foo\r\n",6); + ret = redisReaderGetReply(reader,NULL); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0); + redisReaderFree(reader); + + reader = redisReaderCreate(); + test("Can handle arbitrarily nested multi-bulks: "); + for (i = 0; i < 128; i++) { + redisReaderFeed(reader,(char*)"*1\r\n", 4); + } + redisReaderFeed(reader,(char*)"$6\r\nLOLWUT\r\n",12); + ret = redisReaderGetReply(reader,&reply); + root = reply; /* Keep track of the root reply */ + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_ARRAY && + ((redisReply*)reply)->elements == 1); + + test("Can parse arbitrarily nested multi-bulks correctly: "); + while(i--) { + assert(reply != NULL && ((redisReply*)reply)->type == REDIS_REPLY_ARRAY); + reply = ((redisReply*)reply)->element[0]; + } + test_cond(((redisReply*)reply)->type == REDIS_REPLY_STRING && + !memcmp(((redisReply*)reply)->str, "LOLWUT", 6)); + freeReplyObject(root); + redisReaderFree(reader); + + test("Correctly parses LLONG_MAX: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, ":9223372036854775807\r\n",22); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_INTEGER && + ((redisReply*)reply)->integer == LLONG_MAX); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Set error when > LLONG_MAX: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, ":9223372036854775808\r\n",22); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Bad integer value") == 0); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Correctly parses LLONG_MIN: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, ":-9223372036854775808\r\n",23); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_INTEGER && + ((redisReply*)reply)->integer == LLONG_MIN); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Set error when < LLONG_MIN: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, ":-9223372036854775809\r\n",23); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Bad integer value") == 0); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Set error when array < -1: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "*-2\r\n+asdf\r\n",12); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Set error when bulk < -1: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "$-2\r\nasdf\r\n",11); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Bulk string length out of range") == 0); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Can configure maximum multi-bulk elements: "); + reader = redisReaderCreate(); + reader->maxelements = 1024; + redisReaderFeed(reader, "*1025\r\n", 7); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr, "Multi-bulk length out of range") == 0); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Multi-bulk never overflows regardless of maxelements: "); + size_t bad_mbulk_len = (SIZE_MAX / sizeof(void *)) + 3; + char bad_mbulk_reply[100]; + snprintf(bad_mbulk_reply, sizeof(bad_mbulk_reply), "*%llu\r\n+asdf\r\n", + (unsigned long long) bad_mbulk_len); + + reader = redisReaderCreate(); + reader->maxelements = 0; /* Don't rely on default limit */ + redisReaderFeed(reader, bad_mbulk_reply, strlen(bad_mbulk_reply)); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && strcasecmp(reader->errstr, "Out of memory") == 0); + freeReplyObject(reply); + redisReaderFree(reader); + +#if LLONG_MAX > SIZE_MAX + test("Set error when array > SIZE_MAX: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Set error when bulk > SIZE_MAX: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Bulk string length out of range") == 0); + freeReplyObject(reply); + redisReaderFree(reader); +#endif + + test("Works with NULL functions for reply: "); + reader = redisReaderCreate(); + reader->fn = NULL; + redisReaderFeed(reader,(char*)"+OK\r\n",5); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); + redisReaderFree(reader); + + test("Works when a single newline (\\r\\n) covers two calls to feed: "); + reader = redisReaderCreate(); + reader->fn = NULL; + redisReaderFeed(reader,(char*)"+OK\r",4); + ret = redisReaderGetReply(reader,&reply); + assert(ret == REDIS_OK && reply == NULL); + redisReaderFeed(reader,(char*)"\n",1); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); + redisReaderFree(reader); + + test("Don't reset state after protocol error: "); + reader = redisReaderCreate(); + reader->fn = NULL; + redisReaderFeed(reader,(char*)"x",1); + ret = redisReaderGetReply(reader,&reply); + assert(ret == REDIS_ERR); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && reply == NULL); + redisReaderFree(reader); + + /* Regression test for issue #45 on GitHub. */ + test("Don't do empty allocation for empty multi bulk: "); + reader = redisReaderCreate(); + redisReaderFeed(reader,(char*)"*0\r\n",4); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_ARRAY && + ((redisReply*)reply)->elements == 0); + freeReplyObject(reply); + redisReaderFree(reader); + + /* RESP3 verbatim strings (GitHub issue #802) */ + test("Can parse RESP3 verbatim strings: "); + reader = redisReaderCreate(); + redisReaderFeed(reader,(char*)"=10\r\ntxt:LOLWUT\r\n",17); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_VERB && + !memcmp(((redisReply*)reply)->str,"LOLWUT", 6)); + freeReplyObject(reply); + redisReaderFree(reader); + + /* RESP3 push messages (Github issue #815) */ + test("Can parse RESP3 push messages: "); + reader = redisReaderCreate(); + redisReaderFeed(reader,(char*)">2\r\n$6\r\nLOLWUT\r\n:42\r\n",21); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_PUSH && + ((redisReply*)reply)->elements == 2 && + ((redisReply*)reply)->element[0]->type == REDIS_REPLY_STRING && + !memcmp(((redisReply*)reply)->element[0]->str,"LOLWUT",6) && + ((redisReply*)reply)->element[1]->type == REDIS_REPLY_INTEGER && + ((redisReply*)reply)->element[1]->integer == 42); + freeReplyObject(reply); + redisReaderFree(reader); +} + +static void test_free_null(void) { + void *redisCtx = NULL; + void *reply = NULL; + + test("Don't fail when redisFree is passed a NULL value: "); + redisFree(redisCtx); + test_cond(redisCtx == NULL); + + test("Don't fail when freeReplyObject is passed a NULL value: "); + freeReplyObject(reply); + test_cond(reply == NULL); +} + +static void *hi_malloc_fail(size_t size) { + (void)size; + return NULL; +} + +static void *hi_calloc_fail(size_t nmemb, size_t size) { + (void)nmemb; + (void)size; + return NULL; +} + +static void *hi_realloc_fail(void *ptr, size_t size) { + (void)ptr; + (void)size; + return NULL; +} + +static void test_allocator_injection(void) { + hiredisAllocFuncs ha = { + .mallocFn = hi_malloc_fail, + .callocFn = hi_calloc_fail, + .reallocFn = hi_realloc_fail, + .strdupFn = strdup, + .freeFn = free, + }; + + // Override hiredis allocators + hiredisSetAllocators(&ha); + + test("redisContext uses injected allocators: "); + redisContext *c = redisConnect("localhost", 6379); + test_cond(c == NULL); + + test("redisReader uses injected allocators: "); + redisReader *reader = redisReaderCreate(); + test_cond(reader == NULL); + + // Return allocators to default + hiredisResetAllocators(); +} + +#define HIREDIS_BAD_DOMAIN "idontexist-noreally.com" +static void test_blocking_connection_errors(void) { + redisContext *c; + struct addrinfo hints = {.ai_family = AF_INET}; + struct addrinfo *ai_tmp = NULL; + + int rv = getaddrinfo(HIREDIS_BAD_DOMAIN, "6379", &hints, &ai_tmp); + if (rv != 0) { + // Address does *not* exist + test("Returns error when host cannot be resolved: "); + // First see if this domain name *actually* resolves to NXDOMAIN + c = redisConnect(HIREDIS_BAD_DOMAIN, 6379); + test_cond( + c->err == REDIS_ERR_OTHER && + (strcmp(c->errstr, "Name or service not known") == 0 || + strcmp(c->errstr, "Can't resolve: " HIREDIS_BAD_DOMAIN) == 0 || + strcmp(c->errstr, "Name does not resolve") == 0 || + strcmp(c->errstr, "nodename nor servname provided, or not known") == 0 || + strcmp(c->errstr, "No address associated with hostname") == 0 || + strcmp(c->errstr, "Temporary failure in name resolution") == 0 || + strcmp(c->errstr, "hostname nor servname provided, or not known") == 0 || + strcmp(c->errstr, "no address associated with name") == 0 || + strcmp(c->errstr, "No such host is known. ") == 0)); + redisFree(c); + } else { + printf("Skipping NXDOMAIN test. Found evil ISP!\n"); + freeaddrinfo(ai_tmp); + } + +#ifndef _WIN32 + test("Returns error when the port is not open: "); + c = redisConnect((char*)"localhost", 1); + test_cond(c->err == REDIS_ERR_IO && + strcmp(c->errstr,"Connection refused") == 0); + redisFree(c); + + test("Returns error when the unix_sock socket path doesn't accept connections: "); + c = redisConnectUnix((char*)"/tmp/idontexist.sock"); + test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */ + redisFree(c); +#endif +} + +/* Dummy push handler */ +void push_handler(void *privdata, void *reply) { + int *counter = privdata; + freeReplyObject(reply); + *counter += 1; +} + +/* Dummy function just to test setting a callback with redisOptions */ +void push_handler_async(redisAsyncContext *ac, void *reply) { + (void)ac; + (void)reply; +} + +static void test_resp3_push_handler(redisContext *c) { + redisPushFn *old = NULL; + redisReply *reply; + void *privdata; + int n = 0; + + /* Switch to RESP3 and turn on client tracking */ + send_hello(c, 3); + send_client_tracking(c, "ON"); + privdata = c->privdata; + c->privdata = &n; + + reply = redisCommand(c, "GET key:0"); + assert(reply != NULL); + freeReplyObject(reply); + + test("RESP3 PUSH messages are handled out of band by default: "); + reply = redisCommand(c, "SET key:0 val:0"); + test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS); + freeReplyObject(reply); + + assert((reply = redisCommand(c, "GET key:0")) != NULL); + freeReplyObject(reply); + + old = redisSetPushCallback(c, push_handler); + test("We can set a custom RESP3 PUSH handler: "); + reply = redisCommand(c, "SET key:0 val:0"); + test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && n == 1); + freeReplyObject(reply); + + /* Unset the push callback and generate an invalidate message making + * sure it is not handled out of band. */ + test("With no handler, PUSH replies come in-band: "); + redisSetPushCallback(c, NULL); + assert((reply = redisCommand(c, "GET key:0")) != NULL); + freeReplyObject(reply); + assert((reply = redisCommand(c, "SET key:0 invalid")) != NULL); + test_cond(reply->type == REDIS_REPLY_PUSH); + freeReplyObject(reply); + + test("With no PUSH handler, no replies are lost: "); + assert(redisGetReply(c, (void**)&reply) == REDIS_OK); + test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS); + freeReplyObject(reply); + + /* Return to the originally set PUSH handler */ + assert(old != NULL); + redisSetPushCallback(c, old); + + /* Switch back to RESP2 and disable tracking */ + c->privdata = privdata; + send_client_tracking(c, "OFF"); + send_hello(c, 2); +} + +redisOptions get_redis_tcp_options(struct config config) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, config.tcp.host, config.tcp.port); + return options; +} + +static void test_resp3_push_options(struct config config) { + redisAsyncContext *ac; + redisContext *c; + redisOptions options; + + test("We set a default RESP3 handler for redisContext: "); + options = get_redis_tcp_options(config); + assert((c = redisConnectWithOptions(&options)) != NULL); + test_cond(c->push_cb != NULL); + redisFree(c); + + test("We don't set a default RESP3 push handler for redisAsyncContext: "); + options = get_redis_tcp_options(config); + assert((ac = redisAsyncConnectWithOptions(&options)) != NULL); + test_cond(ac->c.push_cb == NULL); + redisAsyncFree(ac); + + test("Our REDIS_OPT_NO_PUSH_AUTOFREE flag works: "); + options = get_redis_tcp_options(config); + options.options |= REDIS_OPT_NO_PUSH_AUTOFREE; + assert((c = redisConnectWithOptions(&options)) != NULL); + test_cond(c->push_cb == NULL); + redisFree(c); + + test("We can use redisOptions to set a custom PUSH handler for redisContext: "); + options = get_redis_tcp_options(config); + options.push_cb = push_handler; + assert((c = redisConnectWithOptions(&options)) != NULL); + test_cond(c->push_cb == push_handler); + redisFree(c); + + test("We can use redisOptions to set a custom PUSH handler for redisAsyncContext: "); + options = get_redis_tcp_options(config); + options.async_push_cb = push_handler_async; + assert((ac = redisAsyncConnectWithOptions(&options)) != NULL); + test_cond(ac->push_cb == push_handler_async); + redisAsyncFree(ac); +} + +void free_privdata(void *privdata) { + struct privdata *data = privdata; + data->dtor_counter++; +} + +static void test_privdata_hooks(struct config config) { + struct privdata data = {0}; + redisOptions options; + redisContext *c; + + test("We can use redisOptions to set privdata: "); + options = get_redis_tcp_options(config); + REDIS_OPTIONS_SET_PRIVDATA(&options, &data, free_privdata); + assert((c = redisConnectWithOptions(&options)) != NULL); + test_cond(c->privdata == &data); + + test("Our privdata destructor fires when we free the context: "); + redisFree(c); + test_cond(data.dtor_counter == 1); +} + +static void test_blocking_connection(struct config config) { + redisContext *c; + redisReply *reply; + int major; + + c = do_connect(config); + + test("Is able to deliver commands: "); + reply = redisCommand(c,"PING"); + test_cond(reply->type == REDIS_REPLY_STATUS && + strcasecmp(reply->str,"pong") == 0) + freeReplyObject(reply); + + test("Is a able to send commands verbatim: "); + reply = redisCommand(c,"SET foo bar"); + test_cond (reply->type == REDIS_REPLY_STATUS && + strcasecmp(reply->str,"ok") == 0) + freeReplyObject(reply); + + test("%%s String interpolation works: "); + reply = redisCommand(c,"SET %s %s","foo","hello world"); + freeReplyObject(reply); + reply = redisCommand(c,"GET foo"); + test_cond(reply->type == REDIS_REPLY_STRING && + strcmp(reply->str,"hello world") == 0); + freeReplyObject(reply); + + test("%%b String interpolation works: "); + reply = redisCommand(c,"SET %b %b","foo",(size_t)3,"hello\x00world",(size_t)11); + freeReplyObject(reply); + reply = redisCommand(c,"GET foo"); + test_cond(reply->type == REDIS_REPLY_STRING && + memcmp(reply->str,"hello\x00world",11) == 0) + + test("Binary reply length is correct: "); + test_cond(reply->len == 11) + freeReplyObject(reply); + + test("Can parse nil replies: "); + reply = redisCommand(c,"GET nokey"); + test_cond(reply->type == REDIS_REPLY_NIL) + freeReplyObject(reply); + + /* test 7 */ + test("Can parse integer replies: "); + reply = redisCommand(c,"INCR mycounter"); + test_cond(reply->type == REDIS_REPLY_INTEGER && reply->integer == 1) + freeReplyObject(reply); + + test("Can parse multi bulk replies: "); + freeReplyObject(redisCommand(c,"LPUSH mylist foo")); + freeReplyObject(redisCommand(c,"LPUSH mylist bar")); + reply = redisCommand(c,"LRANGE mylist 0 -1"); + test_cond(reply->type == REDIS_REPLY_ARRAY && + reply->elements == 2 && + !memcmp(reply->element[0]->str,"bar",3) && + !memcmp(reply->element[1]->str,"foo",3)) + freeReplyObject(reply); + + /* m/e with multi bulk reply *before* other reply. + * specifically test ordering of reply items to parse. */ + test("Can handle nested multi bulk replies: "); + freeReplyObject(redisCommand(c,"MULTI")); + freeReplyObject(redisCommand(c,"LRANGE mylist 0 -1")); + freeReplyObject(redisCommand(c,"PING")); + reply = (redisCommand(c,"EXEC")); + test_cond(reply->type == REDIS_REPLY_ARRAY && + reply->elements == 2 && + reply->element[0]->type == REDIS_REPLY_ARRAY && + reply->element[0]->elements == 2 && + !memcmp(reply->element[0]->element[0]->str,"bar",3) && + !memcmp(reply->element[0]->element[1]->str,"foo",3) && + reply->element[1]->type == REDIS_REPLY_STATUS && + strcasecmp(reply->element[1]->str,"pong") == 0); + freeReplyObject(reply); + + /* Make sure passing NULL to redisGetReply is safe */ + test("Can pass NULL to redisGetReply: "); + assert(redisAppendCommand(c, "PING") == REDIS_OK); + test_cond(redisGetReply(c, NULL) == REDIS_OK); + + get_redis_version(c, &major, NULL); + if (major >= 6) test_resp3_push_handler(c); + test_resp3_push_options(config); + + test_privdata_hooks(config); + + disconnect(c, 0); +} + +/* Send DEBUG SLEEP 0 to detect if we have this command */ +static int detect_debug_sleep(redisContext *c) { + int detected; + redisReply *reply = redisCommand(c, "DEBUG SLEEP 0\r\n"); + + if (reply == NULL || c->err) { + const char *cause = c->err ? c->errstr : "(none)"; + fprintf(stderr, "Error testing for DEBUG SLEEP (Redis error: %s), exiting\n", cause); + exit(-1); + } + + detected = reply->type == REDIS_REPLY_STATUS; + freeReplyObject(reply); + + return detected; +} + +static void test_blocking_connection_timeouts(struct config config) { + redisContext *c; + redisReply *reply; + ssize_t s; + const char *sleep_cmd = "DEBUG SLEEP 3\r\n"; + struct timeval tv; + + c = do_connect(config); + test("Successfully completes a command when the timeout is not exceeded: "); + reply = redisCommand(c,"SET foo fast"); + freeReplyObject(reply); + tv.tv_sec = 0; + tv.tv_usec = 10000; + redisSetTimeout(c, tv); + reply = redisCommand(c, "GET foo"); + test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, "fast", 4) == 0); + freeReplyObject(reply); + disconnect(c, 0); + + c = do_connect(config); + test("Does not return a reply when the command times out: "); + if (detect_debug_sleep(c)) { + redisAppendFormattedCommand(c, sleep_cmd, strlen(sleep_cmd)); + s = c->funcs->write(c); + tv.tv_sec = 0; + tv.tv_usec = 10000; + redisSetTimeout(c, tv); + reply = redisCommand(c, "GET foo"); +#ifndef _WIN32 + test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && + strcmp(c->errstr, "Resource temporarily unavailable") == 0); +#else + test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_TIMEOUT && + strcmp(c->errstr, "recv timeout") == 0); +#endif + freeReplyObject(reply); + } else { + test_skipped(); + } + + test("Reconnect properly reconnects after a timeout: "); + do_reconnect(c, config); + reply = redisCommand(c, "PING"); + test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); + freeReplyObject(reply); + + test("Reconnect properly uses owned parameters: "); + config.tcp.host = "foo"; + config.unix_sock.path = "foo"; + do_reconnect(c, config); + reply = redisCommand(c, "PING"); + test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); + freeReplyObject(reply); + + disconnect(c, 0); +} + +static void test_blocking_io_errors(struct config config) { + redisContext *c; + redisReply *reply; + void *_reply; + int major, minor; + + /* Connect to target given by config. */ + c = do_connect(config); + get_redis_version(c, &major, &minor); + + test("Returns I/O error when the connection is lost: "); + reply = redisCommand(c,"QUIT"); + if (major > 2 || (major == 2 && minor > 0)) { + /* > 2.0 returns OK on QUIT and read() should be issued once more + * to know the descriptor is at EOF. */ + test_cond(strcasecmp(reply->str,"OK") == 0 && + redisGetReply(c,&_reply) == REDIS_ERR); + freeReplyObject(reply); + } else { + test_cond(reply == NULL); + } + +#ifndef _WIN32 + /* On 2.0, QUIT will cause the connection to be closed immediately and + * the read(2) for the reply on QUIT will set the error to EOF. + * On >2.0, QUIT will return with OK and another read(2) needed to be + * issued to find out the socket was closed by the server. In both + * conditions, the error will be set to EOF. */ + assert(c->err == REDIS_ERR_EOF && + strcmp(c->errstr,"Server closed the connection") == 0); +#endif + redisFree(c); + + c = do_connect(config); + test("Returns I/O error on socket timeout: "); + struct timeval tv = { 0, 1000 }; + assert(redisSetTimeout(c,tv) == REDIS_OK); + int respcode = redisGetReply(c,&_reply); +#ifndef _WIN32 + test_cond(respcode == REDIS_ERR && c->err == REDIS_ERR_IO && errno == EAGAIN); +#else + test_cond(respcode == REDIS_ERR && c->err == REDIS_ERR_TIMEOUT); +#endif + redisFree(c); +} + +static void test_invalid_timeout_errors(struct config config) { + redisContext *c; + + test("Set error when an invalid timeout usec value is given to redisConnectWithTimeout: "); + + config.tcp.timeout.tv_sec = 0; + config.tcp.timeout.tv_usec = 10000001; + + c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); + + test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); + redisFree(c); + + test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: "); + + config.tcp.timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1; + config.tcp.timeout.tv_usec = 0; + + c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); + + test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); + redisFree(c); +} + +/* Wrap malloc to abort on failure so OOM checks don't make the test logic + * harder to follow. */ +void *hi_malloc_safe(size_t size) { + void *ptr = hi_malloc(size); + if (ptr == NULL) { + fprintf(stderr, "Error: Out of memory\n"); + exit(-1); + } + + return ptr; +} + +static void test_throughput(struct config config) { + redisContext *c = do_connect(config); + redisReply **replies; + int i, num; + long long t1, t2; + + test("Throughput:\n"); + for (i = 0; i < 500; i++) + freeReplyObject(redisCommand(c,"LPUSH mylist foo")); + + num = 1000; + replies = hi_malloc_safe(sizeof(redisReply*)*num); + t1 = usec(); + for (i = 0; i < num; i++) { + replies[i] = redisCommand(c,"PING"); + assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS); + } + t2 = usec(); + for (i = 0; i < num; i++) freeReplyObject(replies[i]); + hi_free(replies); + printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0); + + replies = hi_malloc_safe(sizeof(redisReply*)*num); + t1 = usec(); + for (i = 0; i < num; i++) { + replies[i] = redisCommand(c,"LRANGE mylist 0 499"); + assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY); + assert(replies[i] != NULL && replies[i]->elements == 500); + } + t2 = usec(); + for (i = 0; i < num; i++) freeReplyObject(replies[i]); + hi_free(replies); + printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0); + + replies = hi_malloc_safe(sizeof(redisReply*)*num); + t1 = usec(); + for (i = 0; i < num; i++) { + replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000); + assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER); + } + t2 = usec(); + for (i = 0; i < num; i++) freeReplyObject(replies[i]); + hi_free(replies); + printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0); + + num = 10000; + replies = hi_malloc_safe(sizeof(redisReply*)*num); + for (i = 0; i < num; i++) + redisAppendCommand(c,"PING"); + t1 = usec(); + for (i = 0; i < num; i++) { + assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); + assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS); + } + t2 = usec(); + for (i = 0; i < num; i++) freeReplyObject(replies[i]); + hi_free(replies); + printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); + + replies = hi_malloc_safe(sizeof(redisReply*)*num); + for (i = 0; i < num; i++) + redisAppendCommand(c,"LRANGE mylist 0 499"); + t1 = usec(); + for (i = 0; i < num; i++) { + assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); + assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY); + assert(replies[i] != NULL && replies[i]->elements == 500); + } + t2 = usec(); + for (i = 0; i < num; i++) freeReplyObject(replies[i]); + hi_free(replies); + printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); + + replies = hi_malloc_safe(sizeof(redisReply*)*num); + for (i = 0; i < num; i++) + redisAppendCommand(c,"INCRBY incrkey %d", 1000000); + t1 = usec(); + for (i = 0; i < num; i++) { + assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); + assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER); + } + t2 = usec(); + for (i = 0; i < num; i++) freeReplyObject(replies[i]); + hi_free(replies); + printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); + + disconnect(c, 0); +} + +// static long __test_callback_flags = 0; +// static void __test_callback(redisContext *c, void *privdata) { +// ((void)c); +// /* Shift to detect execution order */ +// __test_callback_flags <<= 8; +// __test_callback_flags |= (long)privdata; +// } +// +// static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) { +// ((void)c); +// /* Shift to detect execution order */ +// __test_callback_flags <<= 8; +// __test_callback_flags |= (long)privdata; +// if (reply) freeReplyObject(reply); +// } +// +// static redisContext *__connect_nonblock() { +// /* Reset callback flags */ +// __test_callback_flags = 0; +// return redisConnectNonBlock("127.0.0.1", port, NULL); +// } +// +// static void test_nonblocking_connection() { +// redisContext *c; +// int wdone = 0; +// +// test("Calls command callback when command is issued: "); +// c = __connect_nonblock(); +// redisSetCommandCallback(c,__test_callback,(void*)1); +// redisCommand(c,"PING"); +// test_cond(__test_callback_flags == 1); +// redisFree(c); +// +// test("Calls disconnect callback on redisDisconnect: "); +// c = __connect_nonblock(); +// redisSetDisconnectCallback(c,__test_callback,(void*)2); +// redisDisconnect(c); +// test_cond(__test_callback_flags == 2); +// redisFree(c); +// +// test("Calls disconnect callback and free callback on redisFree: "); +// c = __connect_nonblock(); +// redisSetDisconnectCallback(c,__test_callback,(void*)2); +// redisSetFreeCallback(c,__test_callback,(void*)4); +// redisFree(c); +// test_cond(__test_callback_flags == ((2 << 8) | 4)); +// +// test("redisBufferWrite against empty write buffer: "); +// c = __connect_nonblock(); +// test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1); +// redisFree(c); +// +// test("redisBufferWrite against not yet connected fd: "); +// c = __connect_nonblock(); +// redisCommand(c,"PING"); +// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && +// strncmp(c->error,"write:",6) == 0); +// redisFree(c); +// +// test("redisBufferWrite against closed fd: "); +// c = __connect_nonblock(); +// redisCommand(c,"PING"); +// redisDisconnect(c); +// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && +// strncmp(c->error,"write:",6) == 0); +// redisFree(c); +// +// test("Process callbacks in the right sequence: "); +// c = __connect_nonblock(); +// redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING"); +// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); +// redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING"); +// +// /* Write output buffer */ +// wdone = 0; +// while(!wdone) { +// usleep(500); +// redisBufferWrite(c,&wdone); +// } +// +// /* Read until at least one callback is executed (the 3 replies will +// * arrive in a single packet, causing all callbacks to be executed in +// * a single pass). */ +// while(__test_callback_flags == 0) { +// assert(redisBufferRead(c) == REDIS_OK); +// redisProcessCallbacks(c); +// } +// test_cond(__test_callback_flags == 0x010203); +// redisFree(c); +// +// test("redisDisconnect executes pending callbacks with NULL reply: "); +// c = __connect_nonblock(); +// redisSetDisconnectCallback(c,__test_callback,(void*)1); +// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); +// redisDisconnect(c); +// test_cond(__test_callback_flags == 0x0201); +// redisFree(c); +// } + +int main(int argc, char **argv) { + struct config cfg = { + .tcp = { + .host = "127.0.0.1", + .port = 6379 + }, + .unix_sock = { + .path = "/tmp/redis.sock" + } + }; + int throughput = 1; + int test_inherit_fd = 1; + int skips_as_fails = 0; + int test_unix_socket; + + /* Parse command line options. */ + argv++; argc--; + while (argc) { + if (argc >= 2 && !strcmp(argv[0],"-h")) { + argv++; argc--; + cfg.tcp.host = argv[0]; + } else if (argc >= 2 && !strcmp(argv[0],"-p")) { + argv++; argc--; + cfg.tcp.port = atoi(argv[0]); + } else if (argc >= 2 && !strcmp(argv[0],"-s")) { + argv++; argc--; + cfg.unix_sock.path = argv[0]; + } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) { + throughput = 0; + } else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) { + test_inherit_fd = 0; + } else if (argc >= 1 && !strcmp(argv[0],"--skips-as-fails")) { + skips_as_fails = 1; +#ifdef HIREDIS_TEST_SSL + } else if (argc >= 2 && !strcmp(argv[0],"--ssl-port")) { + argv++; argc--; + cfg.ssl.port = atoi(argv[0]); + } else if (argc >= 2 && !strcmp(argv[0],"--ssl-host")) { + argv++; argc--; + cfg.ssl.host = argv[0]; + } else if (argc >= 2 && !strcmp(argv[0],"--ssl-ca-cert")) { + argv++; argc--; + cfg.ssl.ca_cert = argv[0]; + } else if (argc >= 2 && !strcmp(argv[0],"--ssl-cert")) { + argv++; argc--; + cfg.ssl.cert = argv[0]; + } else if (argc >= 2 && !strcmp(argv[0],"--ssl-key")) { + argv++; argc--; + cfg.ssl.key = argv[0]; +#endif + } else { + fprintf(stderr, "Invalid argument: %s\n", argv[0]); + exit(1); + } + argv++; argc--; + } + +#ifndef _WIN32 + /* Ignore broken pipe signal (for I/O error tests). */ + signal(SIGPIPE, SIG_IGN); + + test_unix_socket = access(cfg.unix_sock.path, F_OK) == 0; + +#else + /* Unix sockets don't exist in Windows */ + test_unix_socket = 0; +#endif + + test_allocator_injection(); + + test_format_commands(); + test_reply_reader(); + test_blocking_connection_errors(); + test_free_null(); + + printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port); + cfg.type = CONN_TCP; + test_blocking_connection(cfg); + test_blocking_connection_timeouts(cfg); + test_blocking_io_errors(cfg); + test_invalid_timeout_errors(cfg); + test_append_formatted_commands(cfg); + if (throughput) test_throughput(cfg); + + printf("\nTesting against Unix socket connection (%s): ", cfg.unix_sock.path); + if (test_unix_socket) { + printf("\n"); + cfg.type = CONN_UNIX; + test_blocking_connection(cfg); + test_blocking_connection_timeouts(cfg); + test_blocking_io_errors(cfg); + if (throughput) test_throughput(cfg); + } else { + test_skipped(); + } + +#ifdef HIREDIS_TEST_SSL + if (cfg.ssl.port && cfg.ssl.host) { + + redisInitOpenSSL(); + _ssl_ctx = redisCreateSSLContext(cfg.ssl.ca_cert, NULL, cfg.ssl.cert, cfg.ssl.key, NULL, NULL); + assert(_ssl_ctx != NULL); + + printf("\nTesting against SSL connection (%s:%d):\n", cfg.ssl.host, cfg.ssl.port); + cfg.type = CONN_SSL; + + test_blocking_connection(cfg); + test_blocking_connection_timeouts(cfg); + test_blocking_io_errors(cfg); + test_invalid_timeout_errors(cfg); + test_append_formatted_commands(cfg); + if (throughput) test_throughput(cfg); + + redisFreeSSLContext(_ssl_ctx); + _ssl_ctx = NULL; + } +#endif + + if (test_inherit_fd) { + printf("\nTesting against inherited fd (%s): ", cfg.unix_sock.path); + if (test_unix_socket) { + printf("\n"); + cfg.type = CONN_FD; + test_blocking_connection(cfg); + } else { + test_skipped(); + } + } + + if (fails || (skips_as_fails && skips)) { + printf("*** %d TESTS FAILED ***\n", fails); + if (skips) { + printf("*** %d TESTS SKIPPED ***\n", skips); + } + return 1; + } + + printf("ALL TESTS PASSED (%d skipped)\n", skips); + return 0; +} diff --git a/ext/hiredis-1.0.2/test.sh b/ext/hiredis-1.0.2/test.sh new file mode 100755 index 000000000..c72bcb0dc --- /dev/null +++ b/ext/hiredis-1.0.2/test.sh @@ -0,0 +1,78 @@ +#!/bin/sh -ue + +REDIS_SERVER=${REDIS_SERVER:-redis-server} +REDIS_PORT=${REDIS_PORT:-56379} +REDIS_SSL_PORT=${REDIS_SSL_PORT:-56443} +TEST_SSL=${TEST_SSL:-0} +SKIPS_AS_FAILS=${SKIPS_AS_FAILS-:0} +SSL_TEST_ARGS= +SKIPS_ARG= + +tmpdir=$(mktemp -d) +PID_FILE=${tmpdir}/hiredis-test-redis.pid +SOCK_FILE=${tmpdir}/hiredis-test-redis.sock + +if [ "$TEST_SSL" = "1" ]; then + SSL_CA_CERT=${tmpdir}/ca.crt + SSL_CA_KEY=${tmpdir}/ca.key + SSL_CERT=${tmpdir}/redis.crt + SSL_KEY=${tmpdir}/redis.key + + openssl genrsa -out ${tmpdir}/ca.key 4096 + openssl req \ + -x509 -new -nodes -sha256 \ + -key ${SSL_CA_KEY} \ + -days 3650 \ + -subj '/CN=Hiredis Test CA' \ + -out ${SSL_CA_CERT} + openssl genrsa -out ${SSL_KEY} 2048 + openssl req \ + -new -sha256 \ + -key ${SSL_KEY} \ + -subj '/CN=Hiredis Test Cert' | \ + openssl x509 \ + -req -sha256 \ + -CA ${SSL_CA_CERT} \ + -CAkey ${SSL_CA_KEY} \ + -CAserial ${tmpdir}/ca.txt \ + -CAcreateserial \ + -days 365 \ + -out ${SSL_CERT} + + SSL_TEST_ARGS="--ssl-host 127.0.0.1 --ssl-port ${REDIS_SSL_PORT} --ssl-ca-cert ${SSL_CA_CERT} --ssl-cert ${SSL_CERT} --ssl-key ${SSL_KEY}" +fi + +cleanup() { + set +e + kill $(cat ${PID_FILE}) + rm -rf ${tmpdir} +} +trap cleanup INT TERM EXIT + +cat > ${tmpdir}/redis.conf <> ${tmpdir}/redis.conf < /* for struct timeval */ + +#ifndef inline +#define inline __inline +#endif + +#ifndef strcasecmp +#define strcasecmp stricmp +#endif + +#ifndef strncasecmp +#define strncasecmp strnicmp +#endif + +#ifndef va_copy +#define va_copy(d,s) ((d) = (s)) +#endif + +#ifndef snprintf +#define snprintf c99_snprintf + +__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) +{ + int count = -1; + + if (size != 0) + count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} + +__inline int c99_snprintf(char* str, size_t size, const char* format, ...) +{ + int count; + va_list ap; + + va_start(ap, format); + count = c99_vsnprintf(str, size, format, ap); + va_end(ap); + + return count; +} +#endif +#endif /* _MSC_VER */ + +#ifdef _WIN32 +#define strerror_r(errno,buf,len) strerror_s(buf,len,errno) +#endif /* _WIN32 */ + +#endif /* _WIN32_HELPER_INCLUDE */ diff --git a/ext/libpqxx-7.7.3/.circleci/config.yml b/ext/libpqxx-7.7.3/.circleci/config.yml new file mode 100644 index 000000000..038d7adb0 --- /dev/null +++ b/ext/libpqxx-7.7.3/.circleci/config.yml @@ -0,0 +1,60 @@ +# CircleCI config for automated test builds triggered from Github. +version: 2 +jobs: + build: + docker: + - image: debian:testing +# - image: postgres:latest + environment: + - PGHOST: "/tmp" + steps: + - checkout + - run: + name: Configure apt archives + command: apt update + - run: + name: Install + command: apt install -y lsb-release python3 cmake postgresql libpq-dev postgresql-server-dev-all build-essential autoconf dh-autoreconf autoconf-archive automake cppcheck + - run: + name: Identify + command: lsb_release -a && c++ --version + - run: + name: Prepare postgres + command: | + mkdir /tmp/db && + chown postgres /tmp/db && + su postgres -c '/usr/lib/postgresql/*/bin/initdb --pgdata /tmp/db --auth trust --nosync' + - run: + name: Run postgres + command: (su postgres -c '/usr/lib/postgresql/*/bin/postgres -D /tmp/db -k /tmp' &) && sleep 5 + - run: + name: Create postgres user + command: su postgres -c "createuser -w -d root" + - run: + name: Set up database + command: createdb root + - run: + name: Autogen + command: NOCONFIGURE=1 ./autogen.sh + - run: + name: Configure + command: | + ./configure \ + --disable-documentation \ + --enable-maintainer-mode \ + --enable-audit \ + --enable-shared --disable-static \ + CXXFLAGS=-O3 + - store_artifacts: + path: config.log + - run: + name: Make + command: make -j$(nproc) + - run: + name: Test + command: PGDATA=db/data make check + - run: + name: Analyse + command: ./tools/lint --full >lint.log + - store_artifacts: + path: lint.log diff --git a/ext/libpqxx-7.7.3/.clang-format b/ext/libpqxx-7.7.3/.clang-format new file mode 100644 index 000000000..97823d8ff --- /dev/null +++ b/ext/libpqxx-7.7.3/.clang-format @@ -0,0 +1,71 @@ +Language: Cpp + +AlignAfterOpenBracket: AlwaysBreak +# AllowAllArgumentsOnNextLine: true +# AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Inline +# AllowShortIfStatementsOnASingleLine: WithoutElse +# AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +# AlwaysBreakTemplateDeclarations: No +BinPackArguments: true +BinPackParameters: true +BreakBeforeBraces: Custom +BraceWrapping: + # AfterCaseLabel: true + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterExternBlock: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyNamespace: false + SplitEmptyRecord: false +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +# BreakInheritanceList: AfterColon +BreakStringLiterals: true +ColumnLimit: 79 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +FixNamespaceComments: true +IncludeBlocks: Preserve +IndentCaseLabels: false +IndentPPDirectives: AfterHash +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 2 +# NamespaceIndentation: All +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +# SpaceBeforeCpp11BracedList: false +# SpaceBeforeCtorInitializerColon: true +# SpaceBeforeInheritanceColon: true +# SpaceBeforeParents: ControlStatements +# SpaceBeforeRangedBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +UseTab: Never +--- diff --git a/ext/libpqxx-7.7.3/.cmake-format b/ext/libpqxx-7.7.3/.cmake-format new file mode 100644 index 000000000..af00ef936 --- /dev/null +++ b/ext/libpqxx-7.7.3/.cmake-format @@ -0,0 +1,184 @@ +format: + _help_max_prefix_chars: + - !!python/unicode 'If the statement spelling length (including space and' + - !!python/unicode 'parenthesis) is larger than the tab width by more than this' + - !!python/unicode 'amount, then force reject un-nested layouts.' + max_prefix_chars: 10 + _help_dangle_align: + - !!python/unicode 'If the trailing parenthesis must be ''dangled'' on its own' + - !!python/unicode 'line, then align it to this reference: `prefix`: the start' + - !!python/unicode 'of the statement, `prefix-indent`: the start of the' + - !!python/unicode 'statement, plus one indentation level, `child`: align to' + - !!python/unicode 'the column of the arguments' + dangle_align: !!python/unicode 'prefix' + _help_max_subgroups_hwrap: + - !!python/unicode 'If an argument group contains more than this many sub-groups' + - !!python/unicode '(parg or kwarg groups) then force it to a vertical layout.' + max_subgroups_hwrap: 2 + _help_min_prefix_chars: + - !!python/unicode 'If the statement spelling length (including space and' + - !!python/unicode 'parenthesis) is smaller than this amount, then force reject' + - !!python/unicode 'nested layouts.' + min_prefix_chars: 4 + _help_max_pargs_hwrap: + - !!python/unicode 'If a positional argument group contains more than this many' + - !!python/unicode 'arguments, then force it to a vertical layout.' + max_pargs_hwrap: 6 + _help_max_lines_hwrap: + - !!python/unicode 'If a candidate layout is wrapped horizontally but it exceeds' + - !!python/unicode 'this many lines, then reject the layout.' + max_lines_hwrap: 2 + _help_autosort: + - !!python/unicode 'If true, the parsers may infer whether or not an argument' + - !!python/unicode 'list is sortable (without annotation).' + autosort: false + _help_line_ending: + - !!python/unicode 'What style line endings to use in the output.' + line_ending: !!python/unicode 'unix' + _help_line_width: + - !!python/unicode 'How wide to allow formatted cmake files' + line_width: 80 + _help_dangle_parens: + - !!python/unicode 'If a statement is wrapped to more than one line, then dangle' + - !!python/unicode 'the closing parenthesis on its own line.' + dangle_parens: true + _help_tab_size: + - !!python/unicode 'How many spaces to tab for indent' + tab_size: 4 + _help_always_wrap: + - !!python/unicode 'A list of command names which should always be wrapped' + always_wrap: [] + _help_require_valid_layout: + - !!python/unicode 'By default, if cmake-format cannot successfully fit' + - !!python/unicode 'everything into the desired linewidth it will apply the' + - !!python/unicode 'last, most agressive attempt that it made. If this flag is' + - !!python/unicode 'True, however, cmake-format will print error, exit with non-' + - !!python/unicode 'zero status code, and write-out nothing' + require_valid_layout: true + _help_keyword_case: + - !!python/unicode 'Format keywords consistently as ''lower'' or ''upper'' case' + keyword_case: !!python/unicode 'unchanged' + _help_layout_passes: + - !!python/unicode 'A dictionary mapping layout nodes to a list of wrap' + - !!python/unicode 'decisions. See the documentation for more information.' + layout_passes: {} + _help_enable_sort: + - !!python/unicode 'If true, the argument lists which are known to be sortable' + - !!python/unicode 'will be sorted lexicographically' + enable_sort: true +_help_markup: !!python/unicode 'Options affecting comment reflow and formatting.' +markup: + _help_literal_comment_pattern: + - !!python/unicode 'If comment markup is enabled, don''t reflow any comment block' + - !!python/unicode 'which matches this (regex) pattern. Default is `None`' + - !!python/unicode '(disabled).' + literal_comment_pattern: null + _help_hashruler_min_length: + - !!python/unicode 'If a comment line starts with at least this many consecutive' + - !!python/unicode 'hash characters, then don''t lstrip() them off. This allows' + - !!python/unicode 'for lazy hash rulers where the first hash char is not' + - !!python/unicode 'separated by space' + hashruler_min_length: 10 + _help_fence_pattern: + - !!python/unicode 'Regular expression to match preformat fences in comments' + - !!python/unicode 'default=r''^\s*([`~]{3}[`~]*)(.*)$''' + fence_pattern: !!python/unicode '^\s*([`~]{3}[`~]*)(.*)$' + _help_canonicalize_hashrulers: + - !!python/unicode 'If true, then insert a space between the first hash char and' + - !!python/unicode 'remaining hash chars in a hash ruler, and normalize its' + - !!python/unicode 'length to fill the column' + canonicalize_hashrulers: true + _help_explicit_trailing_pattern: + - !!python/unicode 'If a comment line matches starts with this pattern then it' + - !!python/unicode 'is explicitly a trailing comment for the preceeding' + - !!python/unicode 'argument. Default is ''#<''' + explicit_trailing_pattern: !!python/unicode '#<' + _help_first_comment_is_literal: + - !!python/unicode 'If comment markup is enabled, don''t reflow the first comment' + - !!python/unicode 'block in each listfile. Use this to preserve formatting of' + - !!python/unicode 'your copyright/license statements.' + first_comment_is_literal: false + _help_enable_markup: + - !!python/unicode 'enable comment markup parsing and reflow' + enable_markup: true + _help_ruler_pattern: + - !!python/unicode 'Regular expression to match rulers in comments' + - !!python/unicode 'default=r''^\s*[^\w\s]{3}.*[^\w\s]{3}$''' + ruler_pattern: !!python/unicode '^\s*[^\w\s]{3}.*[^\w\s]{3}$' + _help_enum_char: + - !!python/unicode 'What character to use as punctuation after numerals in an' + - !!python/unicode 'enumerated list' + enum_char: . + _help_bullet_char: + - !!python/unicode 'What character to use for bulleted lists' + bullet_char: '*' +_help_lint: !!python/unicode 'Options affecting the linter' +lint: + _help_function_pattern: + - !!python/unicode 'regular expression pattern describing valid function names' + function_pattern: !!python/unicode '[0-9a-z_]+' + _help_disabled_codes: + - !!python/unicode 'a list of lint codes to disable' + disabled_codes: [] + _help_min_statement_spacing: + - !!python/unicode 'Require at least this many newlines between statements' + min_statement_spacing: 1 + _help_macro_pattern: + - !!python/unicode 'regular expression pattern describing valid macro names' + macro_pattern: !!python/unicode '[0-9A-Z_]+' + _help_public_var_pattern: + - !!python/unicode 'regular expression pattern describing valid names for' + - !!python/unicode 'publicdirectory variables' + public_var_pattern: !!python/unicode '[0-9A-Z][0-9A-Z_]+' + max_statements: 50 + _help_max_conditionals_custom_parser: + - !!python/unicode 'In the heuristic for C0201, how many conditionals to match' + - !!python/unicode 'within a loop in before considering the loop a parser.' + max_conditionals_custom_parser: 2 + _help_global_var_pattern: + - !!python/unicode 'regular expression pattern describing valid names for' + - !!python/unicode 'variables with global scope' + global_var_pattern: !!python/unicode '[0-9A-Z][0-9A-Z_]+' + _help_keyword_pattern: + - !!python/unicode 'regular expression pattern describing valid names for' + - !!python/unicode 'keywords used in functions or macros' + keyword_pattern: !!python/unicode '[0-9A-Z_]+' + max_arguments: 5 + _help_private_var_pattern: + - !!python/unicode 'regular expression pattern describing valid names for' + - !!python/unicode 'privatedirectory variables' + private_var_pattern: !!python/unicode '_[0-9a-z_]+' + max_localvars: 15 + max_branches: 12 + _help_local_var_pattern: + - !!python/unicode 'regular expression pattern describing valid names for' + - !!python/unicode 'variables with local scope' + local_var_pattern: !!python/unicode '[0-9a-z_]+' + _help_max_statement_spacing: + - !!python/unicode 'Require no more than this many newlines between statements' + max_statement_spacing: 1 + _help_internal_var_pattern: + - !!python/unicode 'regular expression pattern describing valid names for' + - !!python/unicode 'variables with global scope (but internal semantic)' + internal_var_pattern: !!python/unicode '_[0-9A-Z][0-9A-Z_]+' + max_returns: 6 +_help_misc: !!python/unicode 'Miscellaneous configurations options.' +misc: + _help_per_command: + - !!python/unicode 'A dictionary containing any per-command configuration' + - !!python/unicode 'overrides. Currently only `command_case` is supported.' + per_command: {} +_help_parse: !!python/unicode 'Options affecting listfile parsing' +parse: + _help_additional_commands: + - !!python/unicode 'Specify structure for custom cmake functions' + additional_commands: + !!python/unicode 'foo': + !!python/unicode 'flags': + - !!python/unicode 'BAR' + - !!python/unicode 'BAZ' + !!python/unicode 'kwargs': + !!python/unicode 'HEADERS': !!python/unicode '*' + !!python/unicode 'DEPENDS': !!python/unicode '*' + !!python/unicode 'SOURCES': !!python/unicode '*' +_help_encode: !!python/unicode 'Options effecting file encoding' diff --git a/ext/libpqxx-7.7.3/.github/workflows/stale.yml b/ext/libpqxx-7.7.3/.github/workflows/stale.yml new file mode 100644 index 000000000..0983a3e54 --- /dev/null +++ b/ext/libpqxx-7.7.3/.github/workflows/stale.yml @@ -0,0 +1,19 @@ +name: Mark stale issues and pull requests + +on: + schedule: + - cron: "30 1 * * *" + +jobs: + stale: + + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'There has been no activity on this ticket. Consider closing it.' + stale-pr-message: 'There has been no activity on this pull request. Complete it or drop it.' + stale-issue-label: 'no-issue-activity' + stale-pr-label: 'no-pr-activity' diff --git a/ext/libpqxx-7.7.3/.gitignore b/ext/libpqxx-7.7.3/.gitignore new file mode 100644 index 000000000..d798c9dca --- /dev/null +++ b/ext/libpqxx-7.7.3/.gitignore @@ -0,0 +1,49 @@ +autom4te.cache +build-*.out +ChangeLog +CMakeFiles/CMakeTmp +confdefs.h +config.log +config.status +conftest +conftest.cpp +conftest.err +doc/_build +doc/Doxyfile +doc/html/Reference/*.css +doc/html/Reference/*.html +doc/html/Reference/*.js +doc/html/Reference/*.png +doc/html/Reference/*.map +doc/html/Reference/*.md5 +doc/reference-stamp +include/pqxx/config-*-*.h +include/pqxx/config.h +include/pqxx/stamp-h1 +libpqxx.pc +libpqxx-*.tar.gz +libtool +pqxx-config +pqxxlo.txt +test/pqxxlo.txt +tools/pqxxthreadsafety +tools/rmlo +README +win32/common +**/Makefile +**/*.la +**/*.lo +**/*.o +**/*.out +**/.*.swp +**/.swp +**/*.tmp +**/.deps +**/.libs +**/*~ +**/lint.log +**/lint.trs +**/runner +**/runner.log +**/runner.trs +**/test-suite.log diff --git a/ext/libpqxx-7.7.3/.lgtm.yml b/ext/libpqxx-7.7.3/.lgtm.yml new file mode 100644 index 000000000..d53664eb4 --- /dev/null +++ b/ext/libpqxx-7.7.3/.lgtm.yml @@ -0,0 +1,9 @@ +# Config file for lgtm.com static analysis. + +path_classifiers: + test: + - test + generated: + - aclocal.m4 + - configure + - ltmain.sh diff --git a/ext/libpqxx-7.7.3/.lift/ignoreFiles b/ext/libpqxx-7.7.3/.lift/ignoreFiles new file mode 100644 index 000000000..8c6c7a103 --- /dev/null +++ b/ext/libpqxx-7.7.3/.lift/ignoreFiles @@ -0,0 +1,3 @@ +# Make Sonatype Lift ignore these generated files. +configure +config/* diff --git a/ext/libpqxx-7.7.3/AUTHORS b/ext/libpqxx-7.7.3/AUTHORS new file mode 100644 index 000000000..6a922e950 --- /dev/null +++ b/ext/libpqxx-7.7.3/AUTHORS @@ -0,0 +1,4 @@ +Jeroen T. Vermeulen. Wrote the code. +Ray Dassen. Did most of the autoconf etc. stuff. + +Lots of others helped with various other contributions. diff --git a/ext/libpqxx-7.7.3/BUILDING-cmake.md b/ext/libpqxx-7.7.3/BUILDING-cmake.md new file mode 100644 index 000000000..48b1e7930 --- /dev/null +++ b/ext/libpqxx-7.7.3/BUILDING-cmake.md @@ -0,0 +1,272 @@ +Building using CMake +==================== + +The build requires the full PostgreSQL development package. That package must +be installed before you can build libpqxx. + +The instructions will assume that you're working from a command-line shell. +If you prefer to work from an IDE, you'll have to know how your IDE likes to +do things, and you'll want to follow the shell instructions as a guide. + +I'm not too familiar with CMake, and this build relies heavily on contributions +from users. If you see something wrong here, please file a bug and explain, in +simple words, what needs changing and why. + + +Quick start +----------- + +If you just want to get it built and installed quickly, run `cmake` from the +root of the libpqxx source tree. This configures your build. + +Then compile libpqxx by running: + +```shell + cmake --build . +``` + +To install in the default location: + +```shell + cmake --install . +``` + + +Stages +------ + +I'll explain the main build steps in more detail below, but here's a quick +rundown: +1. Configure +2. Compile +3. Test +4. Install +5. Use + +The Test step is optional. + + +Configure +--------- + +Run `cmake` to configure your build. It figures out various parameters, such +as where libpq and its headers are, which C++ features your compiler supports, +and which options your compiler needs. CMake generates configuration for your +build tool: `Makefile`s for `make`, or a Solution (".sln") file for MSVC's +`msbuild`, and so on. + +At this stage you can also override those options yourself. e.g. to instruct +the compiler to look for libpq in a non-standard place, or to use a different +compiler, or pass different compiler flags. Don't try to specify those while +doing the actual compile; set them once when running `cmake`. + +Let's say `$BUILD` is the directory where you want to build libpqxx, and +`$SRC` is where its source code is. So for example, the readme file will be at +`$SRC/README.md`. + +In the simplest case, you just do: + +```shell + cd $BUILD + cmake $SRC +``` + +Add CMake options as needed. There's more about the options below. I'll also +explain the two directories. + + +### Cheat sheet + +Here are some popular `cmake` options for libpqxx: +* `-DSKIP_BUILD_TEST=on` skips compiling libpqxx's tests. +* `-DBUILD_SHARED_LIBS=on` to build a shared library. +* `-DBUILD_SHARED_LIBS=off` to build a static library. +* `-DBUILD_DOC=on` to build documentation. +* `-DINSTALL_TEST=on` to install test executor binary. + +On Windows, I recommend building libpqxx as a shared library and bundling it +with your application. On other platforms I would prefer a static library. + +Building the documentation requires some tools to be installed. It takes at +least Doxygen, but there's no list of requirements. The way to get this set up +is to just try it and see what it's missing. + + +### Generators + +You can also choose your own build tool by telling CMake to use a particular +"generator." For example, here's how to force use of `make`: + +```shell + cmake -G 'Unix Makefiles' +``` + +Or if you prefer to build using `ninja` instead: + +```shell + cmake -G Ninja +``` + +There are many more options. You may prefer yet a different build tool. + + +### Finding libpq + +The CMake step tries to figure out where libpq is, using Cmake's `find_package` +function. If that doesn't work, or if you want a libpq in a different location +from the one it finds, there are two ways to override it. + +The first is to set the individual include and link paths. + +To make the build look for the libpq headers in some directory `$DIR`, add +these options: +* `-DPostgreSQL_TYPE_INCLUDE_DIR=$DIR` +* `-DPostgreSQL_INCLUDE_DIR=$DIR` + +To make the build look for the libpq library binary in a directory `$DIR`, add +this option: +* `-DPostgreSQL_LIBRARY_DIR=$DIR` + +The second, easier way requires CMake 3.12 or better. Here, you specify a path +to a full PostgreSQL build tree. You do this (again for some directory `$DIR`) +by simply passing this cmake option: `-DPostgreSQL_ROOT=$DIR` + + +### Source and Build trees + +Where should you run `cmake`? + +Two directories matter when building libpqxx: the _source tree_ (where the +libpqxx source code is) and the _build tree_ (where you want your build +artefacts). Here I will call them `$SRC` and `$BUILD`, but you can call them +anything you like. + +They can be one and the same, if you like. It's convenient, but less clean, as +source code and build artefacts will exist in the same directory tree. If +you're going to delete the source tree after installing, of course it's fine to +make a mess in there. + + +Compile +------- + +To compile, run: + +```shell + cmake --build $BUILD +``` + +(Where `$BUILD` is again the directory where you wish to do the build.) + +This command will invoke your build tool. Other ways to do the same thing +would be... +* With Unix Makefiles: `make` +* With Ninja: `ninja` +* With Visual Studio: `msbuild libpqxx.sln` +* etc. + +Depending on your build tool, you may want to speed this up by adding an option +like `-j 16`, where `16` is an example of how many processes you might want to +run in parallel. The optimal number depends on your available CPUs and memory. +If you have enough memory, usually the number of CPUs will be a good starting +point for the right number. Don't use this option with Ninja though. It +figures things out for itself. + + +Test +---- + +Of course libpqxx comes with a test suite, to check that the library is +functioning correctly. + +You can run it, but there's one caveat: you need to give it a database where it +can log in, without a password or any other parameters, and try out various +things. + +And when I say you need to "give" it a database, I really mean "give." The +test suite will create and drop tables. Those will all have names prefixed +with "pqxx", so it's probably safe to use a database you already had, but if +any of the items in your database happen to have names starting with `pqxx`, +tough luck. They're fair game. + +Enter this in your shell to build and run the tests: + +```shell + test/runner +``` + + +### Configuring the test database + +But what if you do need a password to log into your test database? Or, what if +it's running on a different system so you need to pass that machine's address? +What if it's not running on the default port? + +You can set these parameters for the test suite, or for any other libpq-based +application, using the following environment variables. (They only set default +values, so they won't override parameters that the application sets in some +other way.) +* `PGHOST` — the IP address where we can contact the database's socket. Or + for a Unix domain socket, its absolute path on the filesystem. +* `PGPORT` — TCP port number on which we can connect to the database. +* `PGDATABASE` — the name of the database to which you wish to connect. +* `PGUSER` — user name under which you wish to log in on the database. +* `PGPASSWORD` — user name's password for accessing the database. + +See the full list at https://www.postgresql.org/docs/current/libpq-envars.html + +**Be careful with passwords,** by the way. Depending on your operating system +and configuration, an attacker with access to your machine could try to read +your password if you set it on the command line: +* Your shell may keep a log of the commands you have entered. +* Environment variables may be visible to other users on the system. + +If at all possible, rely on postgres "peer authentication." Once set up, it is +both more secure and more convenient than passwords. + + +Install +------- + +Once you've built libpqxx, CMake can also help you install the library and +headers on your system. The default installation location will vary from one +operating system to another, but you can set it explicitly. + +Let's say you've got your finished build in `$BUILD`, and you want to install +it to your system's default install location. The command for this is: + +```shell + cmake --install $BUILD +``` + +But you may want to install to some other location. Let's call it `$DEST`. +`$DEST` might be something like `/usr/local` on a Unix-like system, or +something like `D:\Software` on a Windows system. + +To install to `$DEST`, run: + +```shell + cmake --install $BUILD --prefix $DEST +``` + + +Use +--- + +Other projects can include libpqxx in their CMake builds. + +`@abrownsword` uses this configuration: + +```cmake + set(libpqxxdir "libpqxx-${LIBVERSION}") # LIBVERSION set above + set(SKIP_BUILD_TEST on) + set(BUILD_SHARED_LIBS OFF) + + # Used this instead of FindLibrary. + # Setting PostgresSQL_INCLUDE_DIRS externally. + set(PostgreSQL_FOUND true) + set(PostgresSQL_INCLUDE_DIR ${PostgresSQL_INCLUDE_DIRS}) + set(PostgresSQL_TYPE_INCLUDE_DIR ${PostgresSQL_INCLUDE_DIRS}) + + add_subdirectory(${libpqxxdir}) +``` diff --git a/ext/libpqxx-7.7.3/BUILDING-configure.md b/ext/libpqxx-7.7.3/BUILDING-configure.md new file mode 100644 index 000000000..7c63f5b4c --- /dev/null +++ b/ext/libpqxx-7.7.3/BUILDING-configure.md @@ -0,0 +1,275 @@ +Building using `configure` +========================== + +The build requires `libpq`, the C client library for PostgreSQL. This library +must be installed before you can build libpqxx. You'll need the headers as +well as the library binary. + +The instructions will assume that you're working from a command-line shell. +If you prefer to work from an IDE, you'll have to know how your IDE likes to +do things, and you'll want to follow the shell instructions as a guide. + + +Quick start +----------- + +If you just want to get it built and installed quickly, try: + +```shell + ./configure + make + sudo make install +``` + +Want more detail? Read on. + + +Stages +------ + +I'll explain the main build steps in more detail below, but here's a quick +overview: +1. Configure +2. Compile +3. Test +4. Install + +The Test step is optional. + + +Configure +--------- + +The `configure` script configures your build. It figures out various +parameters, such as where libpq and its headers are, which C++ features your +compiler supports, and which options your compiler needs. It generates +Makefiles, which in turn tell the `make` utility how to perform tasks such as +compiling libpqxx, running tests, cleaning up after itself, and installing +libpqxx. + +The `configure` step is also where you can set these options, e.g. to instruct +the compiler to look for libpq in a non-standard place, or to use a different +compiler, or pass different compiler flags. Don't try to specify those while +doing the actual compile; set them once when running `configure`. + +Let's say `$BUILD` is the directory where you want to build libpqxx, and +`$SRC` is where its source code is. So for example, the readme file will be at +`$SRC/README.md`. + +In the simplest case, you just do: + +```shell + cd $BUILD + $SRC/configure +``` + +Add `configure` options as needed. There's more about the options below, or in +the output of `configure --help`. I'll also explain the two directories. + + +### Cheat sheet + +Here are some popular `configure` options: +* `--disable-documentation` skips building of the documentation. +* `CXXFLAGS=-O0` disables optimisation. Slower code, but faster build. +* `CXXFLAGS=-O3` asks for _more_ optimisation. Faster code, slower build. +* `CXX=clang++` compiles with `clang++` as the compiler. +* `--enable-maintainer-mode` makes the compiler more pedantic about the code. +* `--enable-audit` enables expensive run-time checks for debugging. +* `--with-postgres-lib=$DIR` looks for libpq in `$DIR`. +* `--with-postgres-include=$DIR` looks for the libpq headers in `$DIR`. +* `--prefix=$PATH` prepares to install libpqxx in `$PATH`. +* `--enable-shared` enables compilation of libpqxx as a shared library. +* `--disable-shared` disbles compilation of libpqxx as a shared library. +* `--enable-static` enables compilation of libpqxx as a static library. +* `--disable-static` disables compilation of libpqxx as a static library. +* `--help` shows you a lot more of the options. + +So for example, to get a very quick build but produce very inefficient code: + +```shell + ./configure --disable-documentation CXXFLAGS=-O0 +``` + + +Or if you want to pull out all the stops to find problems in the code: + +```shell + ./configure --enable-maintainer-mode --enable-audit CXXFLAGS=-O3 +``` + +(Requesting `-O3` optimisation will make some compilers perform extra analysis +which may, as a side effect, cause them to notice and warn about certain kinds +of mistakes in the code, such as occasionally-unused variables.) + + +### Finding libpq + +One of `configure`'s most important jobs in the libpqxx build is to find the +headers and library for libpq. It has three ways of finding those: +1. Asking a popular tool called `pkg-config`, if installed. +2. Asking postgres' deprecated `pg_config` tool, if installed. +3. Through explicit command-line options to `configure`. + +The explicit command-line options are `--with-postgres-lib` (for the libpq +library binary) and `--with-postgres-include` (for the libpq headers). + +If you want to use a version of libpq that's not installed in a standard +location, e.g. if you're cross-compiling to produce a binary for a different +CPU architecture than your native system's, use the explicit options. + + +### Where does the `configure` script come from? + +I didn't write the `configure` script. It was generated by GNU `autoconf` and +related GNU tools. There's a script to re-generate it, called `autogen.sh`. + +The contents of `configure` are based on a higher-level script called +`configure.ac`. This is where I script checks for specific features in libpq +or the compiler. The `configure` script adds a lot of built-in items that I +don't need to worry about, such as figuring out exactly how your build tools +work. + +Don't try to debug `configure` yourself if you can help it. It's very hard to +read, partly because it's automatically generated, but also because it is +engineered to work with an extremely broad range of shells, compilers, tools, +and operating systems. If you're going to do a "deep dive," try looking at +`configure.ac` instead. + + +### Source and Build trees + +Where should you run `configure`? + +Two directories matter when building libpqxx: the _source tree_ (where the +libpqxx source code is) and the _build tree_ (where you want your build +artefacts). Here I will call them `$SRC` and `$BUILD`, but you can call them +anything you like. + +They can be one and the same, if you like. It's convenient, but less clean, as +source code and build artefacts will exist in the same directory tree. If +you're going to delete the source tree after installing, of course it's fine to +make a mess in there. + + +Compile +------- + +To start the compile, run the `make` tool. It will go through all the steps to +produce a libpqxx library binary. + +Beware though, it only runs _one_ compiler process at a time. That could take +a while. Use the `-j` option to make it run concurrent processes, e.g.: + +```shell + make -j8 +``` + +Very roughly speaking, it's probably fastest if you run one process per CPU +core in your system. If you have the `nproc` utility installed: + +```shell + make -j$(nproc) +``` + +If you want a very fast build and don't mind missing out on efficient code or +documentation, tweak the Configure step above by adding `configure` options +like `CXXFLAGS=-O0` and `--disable-documentation`. + + +Test +---- + +Of course libpqxx comes with a test suite, to check that the library is +functioning correctly. + +You can run it, but there's one caveat: you need to give it a database where it +can log in, without a password or any other parameters, and try out various +things. + +And when I say you need to "give" it a database, I really mean "give." The +test suite will create and drop tables. Those will all have names prefixed +with "pqxx", so it's probably safe to use a database you already had, but if +any of the items in your database happen to have names starting with `pqxx`, +tough luck. They're fair game. + +Enter this in your shell to build and run the tests: + +```shell + make check +``` + +As with compiling, use the `-j` option to make better use of your CPUs. For +example: + +```shell + make check -j$(nproc) +``` + + +### Configuring the test database + +But what if you do need a password to log into your test database? Or, what if +it's running on a different system so you need to pass that machine's address? +What if it's not running on the default port? + +You can set these parameters for the test suite, or for any other libpq-based +application, using the following environment variables. (They only set default +values, so they won't override parameters that the application sets in some +other way.) +* `PGHOST` — the IP address where we can contact the database's socket. Or + for a Unix domain socket, its absolute path on the filesystem. +* `PGPORT` — +* `PGDATABASE` — the name of the database to which you wish to connect. +* `PGUSER` — user name under which you wish to log in on the database. +* `PGPASSWORD` — user name's password for accessing the database. + +See the full list at https://www.postgresql.org/docs/current/libpq-envars.html + +**Be careful with passwords,** by the way. Depending on your operating system +and configuration, an attacker with access to your machine could try to read +your password if you set it on the command line: +* Your shell may keep a log of the commands you have entered. +* Environment variables may be visible to other users on the system. + +If at all possible, rely on postgres "peer authentication." Once set up, it is +both more secure and more convenient than passwords. + + +Install +------- + +Installing libpqxx will install the library and headers in a location chosen at +the time you can the `configure` script. On some systems it defaults to the +`/usr/local/` tree, but it may be different in your environment. Or, use the +`configure` script's `--prefix` option to set an install location. + +(If you want to see exactly what happens, you can run any `make` command line +with the `-n` option, which means: don't actually do this, but print all the +commands you would execute if you did. It's a lot of output though.) + +To install, ensure that you have sufficient privileges to write the files to +their install locations, and run: + +```shell + make install +``` + +Save your build tree somewhere, so that you will be able to undo installation +in the future: + +```shell + make uninstall +``` + +When using the library, make sure the libpqxx headers are in your compiler's +include path. (You will no longer need the libpq headers at that time.) + +Also, building an application which uses libpqxx, make sure the libpqxx library +binary is in your compiler's library search path. And if the library binary is +a shared library, you'll also need it in your loader's search path when running +your application. + +This last part goes for libpq as well: when using libpq, make sure you have +the libpq library binary in your compiler's library search path, and if it's a +shared library, also have it in your loader's search path when running. diff --git a/ext/libpqxx-7.7.3/CMakeLists.txt b/ext/libpqxx-7.7.3/CMakeLists.txt new file mode 100644 index 000000000..55b3c4d44 --- /dev/null +++ b/ext/libpqxx-7.7.3/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required(VERSION 3.8) + +file(READ VERSION VER_FILE_CONTENT) +string(STRIP ${VER_FILE_CONTENT} VER_FILE_CONTENT) + +project( + libpqxx + VERSION ${VER_FILE_CONTENT} + LANGUAGES CXX +) + +if(NOT "${CMAKE_CXX_STANDARD}") + set(CMAKE_CXX_STANDARD 17) +endif() +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) + +option(BUILD_DOC "Build documentation" OFF) + +if(NOT SKIP_BUILD_TEST) + option(BUILD_TEST "Build all test cases" ON) +endif() + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) +include(config) + +add_subdirectory(src) +add_subdirectory(include) +if(BUILD_DOC) + add_subdirectory(doc) +endif() +if(BUILD_TEST) + add_subdirectory(test) +endif() + +# installation +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/libpqxx-config-version.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) +install(FILES cmake/libpqxx-config.cmake + "${CMAKE_CURRENT_BINARY_DIR}/libpqxx-config-version.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libpqxx +) +install( + EXPORT libpqxx-targets + NAMESPACE libpqxx:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libpqxx +) +# Build tree export +export( + EXPORT libpqxx-targets + NAMESPACE libpqxx:: + FILE ${CMAKE_CURRENT_BINARY_DIR}/libpqxx-targets.cmake +) +configure_file( + cmake/libpqxx-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/libpqxx-config.cmake + COPYONLY +) +# Package generation +set(CPACK_GENERATOR TGZ) +set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) +include(CPack) diff --git a/ext/libpqxx-7.7.3/COPYING b/ext/libpqxx-7.7.3/COPYING new file mode 100644 index 000000000..8a566aabe --- /dev/null +++ b/ext/libpqxx-7.7.3/COPYING @@ -0,0 +1,27 @@ +Copyright (c) 2000-2022 Jeroen T. Vermeulen. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the author, nor the names of other contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/ext/libpqxx-7.7.3/INSTALL b/ext/libpqxx-7.7.3/INSTALL new file mode 100644 index 000000000..1c03a1c3b --- /dev/null +++ b/ext/libpqxx-7.7.3/INSTALL @@ -0,0 +1,2 @@ +For installation instructions, see `BUILDING-configure.md` (for the `configure` +build) and `BUILDING-cmake.md` (for the CMake build). diff --git a/ext/libpqxx-7.7.3/Makefile.am b/ext/libpqxx-7.7.3/Makefile.am new file mode 100644 index 000000000..cd93a57ef --- /dev/null +++ b/ext/libpqxx-7.7.3/Makefile.am @@ -0,0 +1,23 @@ +SUBDIRS = include src test tools config doc +EXTRA_DIST = autogen.sh configitems README.md VERSION requirements.json + +MAINTAINERCLEANFILES = \ + Makefile.in aclocal.m4 config.h.in config.log configure stamp-h.in + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libpqxx.pc + +TESTS = tools/lint + + +# Generate ChangeLog from git history. It goes all the way back through +# the project's git, bzr, svn, and cvs days. +dist-hook: ChangeLog + +ChangeLog: configure.ac + git log --stat --name-only --date=short --abbrev-commit >$@ + + +# We use README.md, but automake expects plain README. +README: README.md + ln -s $< $@ diff --git a/ext/libpqxx-7.7.3/Makefile.in b/ext/libpqxx-7.7.3/Makefile.in new file mode 100644 index 000000000..f6798aa75 --- /dev/null +++ b/ext/libpqxx-7.7.3/Makefile.in @@ -0,0 +1,1253 @@ +# Makefile.in generated by automake 1.16.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/config/m4/libtool.m4 \ + $(top_srcdir)/config/m4/ltoptions.m4 \ + $(top_srcdir)/config/m4/ltsugar.m4 \ + $(top_srcdir)/config/m4/ltversion.m4 \ + $(top_srcdir)/config/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(am__DIST_COMMON) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/include/pqxx/config.h +CONFIG_CLEAN_FILES = libpqxx.pc compile_flags +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkgconfigdir)" +DATA = $(pkgconfig_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + cscope check recheck distdir distdir-am dist dist-all \ + distcheck +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/config/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/config/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/compile_flags.in \ + $(srcdir)/libpqxx.pc.in $(top_srcdir)/config/compile \ + $(top_srcdir)/config/config.guess \ + $(top_srcdir)/config/config.sub \ + $(top_srcdir)/config/install-sh $(top_srcdir)/config/ltmain.sh \ + $(top_srcdir)/config/missing \ + $(top_srcdir)/config/mkinstalldirs \ + $(top_srcdir)/config/test-driver AUTHORS COPYING ChangeLog \ + INSTALL NEWS README README.md +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +# Exists only to be overridden by the user if desired. +AM_DISTCHECK_DVI_TARGET = dvi +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PG_CONFIG = @PG_CONFIG@ +PKG_CONFIG = @PKG_CONFIG@ +POSTGRES_INCLUDE = @POSTGRES_INCLUDE@ +PQXXVERSION = @PQXXVERSION@ +PQXX_ABI = @PQXX_ABI@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_postgres_lib = @with_postgres_lib@ +SUBDIRS = include src test tools config doc +EXTRA_DIST = autogen.sh configitems README.md VERSION requirements.json +MAINTAINERCLEANFILES = \ + Makefile.in aclocal.m4 config.h.in config.log configure stamp-h.in + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libpqxx.pc +TESTS = tools/lint +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .log .test .test$(EXEEXT) .trs +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): +libpqxx.pc: $(top_builddir)/config.status $(srcdir)/libpqxx.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +compile_flags: $(top_builddir)/config.status $(srcdir)/compile_flags.in + cd $(top_builddir) && $(SHELL) ./config.status $@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool config.lt +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +tools/lint.log: tools/lint + @p='tools/lint'; \ + b='tools/lint'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-zstd: distdir + tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + *.tar.zst*) \ + zstd -dc $(distdir).tar.zst | $(am__untar) ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build/sub \ + && ../../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=../.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(DATA) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(pkgconfigdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-pkgconfigDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-pkgconfigDATA + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--refresh check check-TESTS check-am clean clean-cscope \ + clean-generic clean-libtool cscope cscopelist-am ctags \ + ctags-am dist dist-all dist-bzip2 dist-gzip dist-hook \ + dist-lzip dist-shar dist-tarZ dist-xz dist-zip dist-zstd \ + distcheck distclean distclean-generic distclean-libtool \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkgconfigDATA install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am recheck tags tags-am uninstall uninstall-am \ + uninstall-pkgconfigDATA + +.PRECIOUS: Makefile + + +# Generate ChangeLog from git history. It goes all the way back through +# the project's git, bzr, svn, and cvs days. +dist-hook: ChangeLog + +ChangeLog: configure.ac + git log --stat --name-only --date=short --abbrev-commit >$@ + +# We use README.md, but automake expects plain README. +README: README.md + ln -s $< $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ext/libpqxx-7.7.3/NEWS b/ext/libpqxx-7.7.3/NEWS new file mode 100644 index 000000000..102716c2e --- /dev/null +++ b/ext/libpqxx-7.7.3/NEWS @@ -0,0 +1,1040 @@ +7.7.3 + - Fix up more damage done by auto-formatting. + - New `result::for_each()`: simple iteration and conversion of rows. (#528) + - Add some missing headers in ``. (#551) + - More strictness in `header-pre.hxx`/`header-post.hxx` checking. + - Disallow nesting of `ignore-deprecated` blocks. + - Deprecate `exec` functions' `desc` parameter. + - Fix `placeholders` documentation. (#557) + - Strip `const` and references from `value_type`. (#558) + - Get tests running on appveyor. (#560) + - Fix broken nonblocking connection on Windows. (#560) +7.7.2 + - Fix up damage done by auto-formatting. +7.7.1 + - When you build libpqxx, configure the compiler's C++ version yourself! + - Finally fix a long-standing silly warning in autogen. + - Fix `digit_to_number` not being found on some comilers. (#518, #519) + - In audit mode, define `_FORTIFY_SOURCE` to enable some extra checks. + - Make more functions `constexpr`. Nothing particularly useful though. + - Make more functions `noexcept`. + - Move constructor & assignment for `result`. + - Support LGTM.com and Facebook "infer" static analysis. + - Deprecated `set_variable`/`get_variable` on `transaction_base`. + - (Design unearthed warts in SQL variables, which were then fixed.) + - Set/get session variables on `connection`: `set_session_var`/`get_var`. + - Set/get local variables: execute SQL statements. + - When using `select()`, include `` if available. +7.7.0 + - Fix `stream_to` for differing table/client encodings. (#473) + - Use `[[likely]]` & `[[unlikely]]` only in C++20, to silence warnings. + - Fix clang "not a const expression" error in Windows. (#472) + - Fix warnings about `[[likely]]` in `if constexpr`. (#475) + - Clearer error for ambiguous string conversion of `char` type. (#481) + - Pseudo-statement in `prepare()` error was for the wrong statement. (#488) + - New class, `connecting` for nonblocking connection to the database. (#487) + - New class, `range` for SQL range types. (#490) + - Replace `std::isdigit` with a safer alternative. + - Support string conversions for `std::chrono::year_month_day`. (#492) + - Helper for implementing string traits: `generic_to_buf`. + - Support `result::at(row_num, col_num)`. + - Support `result[row_num, col_num]` if the compiler allows it. + - Work around broken `std::filesystem::path` in MinGW. (#498) + - Fix leak when getting client encoding fails. (#500) + - Use `std::cmp_greater` etc. when available. Saves some ugly casts. + - Move `result_iterator.hxx` into `pqxx/include/internal/`. + - Move `compiler-public.hxx` into `pqxx/include/internal/`. + - Add script for updating copyright strings. + - Make `tools/lint` figure out source directory by itself. + - Pass the actual C++ version to `tools/lint`, not the baseline one. + - Re-enable pyflakes testing in `tools/lint`. + - Make more functions `[[nodiscard]]`. + - Qualified some member functions as lvalue or rvalue. + - Don't run clang-tidy by default. Compatibility issues with gcc options. + - Describe version requirements in a JSON file, `requirements.json`. + - Doxygen documentation now uses the (Doxygen-extended) Markdown format. + - Build docs in `doc/html/`, no longer in `doc/html/Reference/`. + - Disable some `std::filesystem` features on Windows. + - Shut up stupid Visual Studio warnings. + - On gcc, mark rarely-used functions as "cold," to be optimised for size. + - Glyph scanning for GB18030 encoding was utterly broken. (#517) +7.6.0 + - Removed bad string conversion to `std::basic_string_view`. (#463) + - Add C++20 concepts: `binary`, `char_string`, `char_strings`. + - Generalise binary strings to any contiguous range of `std::byte`. + - Mark `zview` as a view and as a borrowed range. + - Save a copy step on string fields in `stream_to`. + - New helper: `pqxx::value_type`. + - New helper: `pqxx::binary_cast`. (#450) + - Some escaping functions now have "into my buffer" variants. + - More generic escaping functions, supporting more types of binary. + - In C++20, accept generic columns list for `stream_to`. (#447) + - Renamed `` to ``. + - Deprecated `dynamic_params` in favour of `params`. + - `pqxx::params::append_multi()` now calls `reserve()` if possible. + - `pqxx::params::size()` is now `noexcept` (but sadly, `ssize()` is not). + - Helper for generating parameter placeholders `$1`, `$2`, etc. (#443) + - Now requires support for C++14 `[[deprecated]]` attribute. + - Deprecated `unesc_raw()` with `unesc_bin()` variants. + - Once `unesc_raw()` is gone, we'll support only the hex escape format. + - Work around broken `thread_local` in MinGW gcc < 11.1. + - `pqxx::blob` now supports `std::filesystem::path`. + - Fixed check against header/lib version mismatch: `check_pqxx_version_7_6` + - Deprecated result slicing. Expect `row::slice()` to disappear. + - More complete documentation, of cursors in particular. +7.5.2 + - **Actual serious bug.** `blob::read(std::vector<...>)` was broken. +7.5.1 + - Fixed some Visual Studio warnings. + - Missed a bit in working around MinGW's broken ``. + - Deprecated more obsolete representations of binary data. Use `std::byte`. + - New script `tools/deprecations` lists files that use deprecated code. + - Added automake-generated `config/compile` to revision control. +7.5.0 + - Now requires `std::variant` support! No longer works with gcc7. + - When implementing a string conversion, consider specialising `param_format`. + - Stop "aborting" nontransaction on closing. (#419) + - Silence an overzealous Visual C++ warning. (#418) + - Starting support for C++20 `std::span`. + - New `blob::read()` using `std::span`. (#429) + - New `params` class lets you build parameter lists incrementally. (#387) + - Pass `std::vector` params in binary format. + - Dropped legacy tests 31 and 49 to work around clang C++20 bug. + - Fixed `stream_to` test failure in non-English locales. (#440) + - Clarify `transaction_base::stream` documentation. (#423) + - Avoid `` on MinGW; it's broken there. (#336, #398, #424, #441) +7.4.1 + - Missing includes; broke macOS clang build. (#416) +7.4.0 + - Work around Visual Studio 2017 bug with `[[deprecated]]`. (#405, #406) + - Work around eternal Windows bug with `max` macro yet again. (#101) + - Prepare for C++20 `std::ssize()`. + - Dropped test12, which didn't add much and tried to read null strings. + - Support string conversion for `std::monostate`. (#409) + - Consistent "named constructors" for `stream_to` and `stream_from`. + - New `table_path` type. + - New `connection` methods `quote_table` and `quote_columns`. + - Lots of deprecated stream constructors. + - `pqxx::row::swap()` now finally has the `deprecated` attribute. + - Finally deprecated a bunch of `field`, `row`, and `result` constructors. + - Exposed `transaction_focus` marker class. +7.3.1 + - New, simpler API for large objects: `blob` ("binary large object"). + - `largeobject` and friends are now deprecated. + - Fix visibility issue on gcc/clang, especially on macOS. (#395) + - Use "pure" & "visibility" attributes if they work, regardless of compiler. + - More deprecated items now have the `[[deprecated]]` attribute. + - Document the concept of transaction focus. + - Error messages more often report query description, if given. + - Suppress spurious deprecation messages on Visual Studio. (#402) + - Correct error when prepared/param statement clashes with tx focus. (#401) + - `from_stream` with `from_query` now has a convenient factory function. + - Removed some obsolete scripts from the `tools/` directory. +7.3.0 + - `stream_to` now quotes and escapes its table name. + - Removed `transaction_base::classname()`. Did anyone ever use it? + - Internal reorg of the `transaction` and `transactionfocus` hierarchies. + - Removed the only case of virtual inheritance, related to `namedclass`. + - Internal `concat()` for faster, simpler string concatentation. + - Fix compile omission in string conversions for `nullptr_t`. + - `pqxx::size_buffer()` can now size multiple values at once. + - `multi_to_string()` to convert multiple values into one `std::string`. + - Implicit `zview` constructor from `char const *`. (#389) + - Many `std::string&` parameters are now `zview` or `std::string_view`. + - Now checking statement parameter lengths for overflow. + - `#include ` in connection.cxx. (#394) +7.2.1 + - Fix infinite loop in converting `char *` to string. (#377) + - Deprecated `namedclass`. + - Convert an entire row using `row::as()`. + - Internal rework of `field::to()` and `field::as()` functions. + - Some more warning options in maintainer mode. + - Removed the old, DocBook-based tutorial. + - Fixed wrong `query` and SQLSTATE params to some exceptions. (#378) +7.2.0 + - You can now implicitly convert a `const std::string &` to `zview`. + - Replaced some overloads for C strings and C++ strings with `zview`. + - Deprecating `binarystring`. Use `std::basic_string` instead! + - Array parser did not recognise escaping in unquoted values. + - gcc10 test build fix: a result iterator is not the same thing as a `row`. + - Doc fix: field size does _not_ include terminating zero. (#356) + - Fix error message in `demangle_type_name`: printed result, not raw name. + - Fix compile warning in `demangle_type_name` on GNU systems. + - Document that string conversions assume non-null values. + - Start playing with C++20 _concepts._ + - Sketch out concepts-based `PQconnectdbParams` support. (#343) + - Add missing link to "datatypes" documentation. (#346) + - Supports `to_string`, `stream_to`, etc. for `binarystring`. (#312) + - Fixed infinite recursion when using `std::optional` in `stream_to`. (#364) + - Home-rolled hex-escaping. Saves an allocation. + - Catch floating-point negative overflow in `check_cast`, not underflow. + - Bit more work on CMake build doc. (#318) + - Typo in `datatypes.md`: `nullness`, not `nullness_traits`. (#353) + - Fixed test names map in `tests/runner.cxx`. (#354) + - Integral `from_string` now accept leading whitespace, as in composite types. + - Experimental support basics for composite types. (#355) + - Use `stream_from` without knowing the number of fields. (#357) + - Global `size_buffer` function. + - `quote()` now works for always-null types without conversions defined. + - `std::nullopt` now converts to an SQL null. + - Skip quoting and escaping array/composite fields of "safe" types. + - New type trait: `is_unquoted_safe`. + - Forbid invalid specialisations of `query_value`. + - Fixed `mktemp` invocation that broke on FreeBSD. + - Avoid unneeded encode/decode step on more binary data. + - If `__cxa_demangle` fails, fall back on raw type name. (#361) +7.1.2 + - Document build in `BUILDING-configure.md` / `BUILDING-cmake.md`. + - Work around silly clang warning in `test_errorhandler.cxx`. + - Fix install error with some internal headers. (#322) + - Fix "No object selected" error message in large objects. (#324) + - If error has no SQLSTATE, throw `broken_connection`. (#280) + - Fix argument order in `encrypt_password`. (#333, #334) + - Fix underestimate of buffer size for `to_string` for floats. (#328) +7.1.1 + - Compile fix for Visual Studio. + - Warning fix for clang. + - Also install `transaction_focus.hxx`. (#320) +7.1.0 + - Query tuples straight into `std::tuple` using `transaction::stream()`! + - And, `stream_from` now supports more or less arbitrary queries. + - Instead of a tuple of fields, you can pass `stream_to` a container as well. + - `string_traits::size_buffer()` must now be `noexcept`. + - New `nullness` member: `always_null`. + - There is now `to_buf` support for string literals. + - Streaming data is now more efficient. + - The table name in `stream_from` is now escaped. + - You can now "convert" strings to `std::string_view`. Mind your lifetimes! + - A `std::variant` will now convert to string, if its member types do. + - If a `stream_from` row fails to convert, you can no longer retry it. + - `from_string(field const &)` now handles null values properly. + - Obsolete Windows build docs are gone. + - Added `row::to(std::tuple<...>)`. + - Unified the test suites. We no longer need `test/unit/unit_runner`. + - New helper: `strip_t<...>` to remove a type's constness and reference. + - Replace custom templating with CMake glob in `src/CMakeLists.txt`. + - Replace custom templating with CMake glob in `doc/CMakeLists.txt`. + - Replace custom templating with CMake glob in `test/unit/CMakeLists.txt`. +7.0.8 + - Inline `type_name` in `PQXX_DECLARE_ENUM_CONVERSION`. +7.0.7 + - Fix broken `--with-postgres-lib` option in `configure` script (#311) + - Should now build even if neither `pkg-config` nor `pg_config` is available. + - CMake accepts `PostgreSQL_ROOT`, if it's a sufficiently recent version. +7.0.6 + - Prefer `pg_config` over `pkg-config` for include path (#291). + - Try to plod on if we don't know the PostgreSQL include path. + - Fix error message when starting overlapping transactions and such (#303). + - Fix potential crashes when converting invalid strings to floats (#307, #308). +7.0.5 + - Compile fix for g++ 10: include `` (#292). + - Cleaned up error-checking of PG results. (#280). + - The `esc()` methods now also take `std::string_view` (#295). +7.0.4 + - Fix possible crash in `connection::connection_string` (#290). + - Fix filtering of default values in `connection::connection_string` (#288). + - Deprecate `row::swap` and public inheritance of iterators from `row`. + - More copy/move/default constructors/assignments on result iterators. + - More copy/move/default constructors/assignments on row iterators. +7.0.3 + - Fixed misreporting of broken connection as `sql_error` (#280). + - Replaced non-ASCII test texts with escape codes (#282, #283). + - `ilostream` could truncate at `0xff` byte at buffer boundary (#284, #286). +7.0.2 + - New query function: `query_value`, queries and converts a single value. + - A `stream_from` stream can now be iterated. + - More callable types qualify as transactors, thanks to `std::invoke`. +7.0.1 + - Windows build fixes. + - Documentation for writing your own string conversions. + - `transaction_rollback` and children are now `sql_error`, not just `failure`. +7.0.0 + - Bumped minimum required C++ version to C++17. + - Everything has changed. If you're porting from older versions, be careful! + - There is now only one connection class: `connection`. + - Removed tablereader/tablewriter, replaced by stream_from/stream_to. + - Removed obsolete transactor framework, replaced by post-C++11 one. + - Removed old ways of invoking parameterised and prepared statements. + - Session variables are no longer cached. + - If you do weird stuff with setting/getting variables, it may break. + - Reading a variable written from raw SQL, procedures, etc, will now work. + - Prepared statements are now registered immediately. + - If you do weird stuff with preparing/unpreparing statements, it may break. + - Changed exceptions and errors for many error situations. + - Mishandling prepared statements will now break the connection. + - Many string references and const char pointers are now `std::string_view`. + - New `zview` class wraps `string_view` for string with terminating zero. + - The `stream_base` abstract base class is gone. + - Transactions no longer have an `isolation_tag` nested type. + - The `isolation_traits` type is gone. + - There's no more `pqxx_exception`. Complicated things too much. + - `pqxx::string_traits<>::name()` has been replaced with `pqxx::type_name`. + - `to_string()` can now handle `std::vector` (to produce an SQL array). + - All `from_string()` that took a C string now take a `std::string_view`. + - There are now separate `string_traits` and `null_traits` templates. + - Enums outside classes are now scoped enums. + - `error_verbosity` is no longer nested in connection. + - `connection::get_verbosity` is gone. + - Some enums have changed names: `accesspolicy` to `access_policy`, and so on. + - `dynamic_params` now accepts an accessor. + - Fixed "const" support in arguments to parameterised/prepared statements. + - Connection objects can now be moved. + - `connections::options()` has been replaced by `connection_string()`. + - Replaced pqxx-fulltest with `test_all.py`. + - Some `size_type` have changed to different types, to match current libpq. + - Internal overflows are more consistently caught and reported. + - Deprecated items have been removed. + - Large objects now require backend version 9.3 or better. + - Seeking inside large objects now supports 64-bit sizes. + - Visual Studio project files and sample headers are gone. Use CMake. + - MinGW Makefiles are gone. Use `configure` or CMake. + - Need PostgreSQL 10 to use robusttransaction. + - `robusttransaction` no longer uses a log table. Feel free to drop it. +6.4.4 + - Use pkg-config if pg-config is not available. + - In CMake build, prefer CMake's config headers over any found in source tree. +6.4.3 + - Updated copyright strings. + - Added missing "stream" headers to autotools-based install. + - Added stream headers to pqxx/pqxx header. +6.4.2 + - Fix mistake for Windows in 6.4.1: use PQfreemem, not std::free. + - Easily enable runtime checks in configure: "--enable-audit". + - Enable optimisation in CircleCI build. +6.4.1 + - Fixed more memory leaks. +6.4.0 + - Half fix, half work around nasty notice processor life time bug. + - Fix selective running of tests: "test/unit/runner test_infinities". + - Added some missing `std::` qualifications. +6.3.3 + - Throw more appropriate error when unable to read client encoding. + - CMake build fixes. +6.3.2 + - Conversion errors no longer throw pqxx::failure; always conversion_error! + - Use C++17's built-in numeric string conversions, if available. + - Query numeric precision in a more sensible, standard way. + - Avoid "dead code" warning. + - Replace obsolete autoconf macros. + - Remove all "using namespace std". + - CMake build fixes. +6.3.1 + - Windows compile fix (CALLBACK is a macro there). + - Work around Visual Studio 2017 not supporting ISO 646. +6.3.0 + - New "table stream" classes by Joseph Durel: stream_from/stream_to. + - Support weird characters in more identifiers: cursors, notifcations, etc. + - Connection policies are deprecated. It'll all be one class in 7.0! + - Connection deactivation/reactivation is deprecated. + - Some items that were documented as deprecated are now also declared as such. + - Fix Windows bug where WSAPoll was never used. Thanks dpolunin. + - Fix Windows CMake build to link to socket libraries. Thanks dpolunin. + - Various other changes to the CMake build. + - Fix failure when doing multiple string conversions with Visual C++. + - Fix nested project builds in CMake. Thanks Andrew Brownsword. + - In Visual Studio, build for host architecture, not "x64". + - Fix string conversion link error in Visual Studio. Thanks Ivan Poiakov. + - Fix string conversion to bool for "1". Thanks Amaracs. + - Fix in escaping of strings in arrays. Thanks smirql. + - Faster copying of results of large queries. Thanks Vsevolod Strukchinsky. + - Starting to handle encodings properly! Thanks to Joseph Durel. + - No longer using std::iterator (deprecated in C++17). +6.2.5 + - Removed deprecated pqxx-config. + - Build fix on Visual C++ when not defining NOMINMAX. + - Reduce setup code for string conversions, hopefully improving speed. + - Allow nul bytes in tablereader. + - Support defining string conversions for enum types. + - Fixed const/pure attributes warning in gcc 8. + - Updated build documentation to mention CMake option. + - Fixed a floating-point string conversion failure with Visual Studio 2017. +6.2.4 + - Fix builds in paths containing non-ASCII characters. + - New macro: PQXX_HIDE_EXP_OPTIONAL (to work around a client build error). +6.2.3 + - Build fixes. +6.2.2 + - Variable number of arguments to prepared or parameterised statement (#75). + - Windows build fix (#76). +6.2.1 + - Compile fixes. +6.2.0 + - At last! A check against version mismatch between library and headers. + - Small compile fixes. +6.1.1 + - Small compile fixes. + - A particular error string would always come out empty. +6.1.0 + - Dependencies among headers have changed. You may need extra includes. + - Library headers now include "*.hxx" directly, not the user-level headers. + - Supports parsing of SQL arrays, when using "ASCII-like" encodings. +6.0.0 + - C++11 is now required. Your compiler must have shared_ptr, noexcept, etc. + - Removed configure.ac.in; we now use configure.ac directly. + - Removed pqxx::items. Use the new C++11 initialiser syntax. + - Removed maketemporary. We weren't using it. + - Can now be built outside the source tree. + - New, simpler, lambda-friendly transactor framework. + - New, simpler, prepared statements and parameterised statements. + - Result rows can be passed around independently. + - New exec0(): perform query, expect zero rows of data. + - New exec1(): perform query, expect (and return) a single row of data. + - New exec_n(): perform query, expect exactly n rows of data. + - No longer defines Visual Studio's NOMINMAX in headers. + - Much faster configure script. + - Most configuration items are gone. + - Retired all existing capability flags. + - Uses WSAPoll() on Windows. + - Documentation on readthedocs.org, thanks Tim Sheerman-Chase. +5.1.0 + - Releases after this will require C++11! + - Internal simplification to pqxx::result. + - A row object now keeps its result object alive. + - New exec() variants: "expect & return 1 row," "expect no rows," "expect n." + - ChangeLog is gone. It was a drag on maintenance. +5.0.1 + - Exposed sqlstate in sql_error exception class. +5.0 + - The PGSTD namespace alias is gone. Use the std namespace directly. + - pqxx::tuple is now pqxx::row, to avoid clashes with std::tuple. + - Deprecated escape_binary functions dropped. + - Deprecated notify_listener class dropped. + - Support for many old compilers dropped. + - Support for "long long" and "long double" types is always enabled. + - No longer uses obsolete std::tr1 namespace; use plain std instead. + - Now requires libpq 9.1 or better. + - Requires server version 9.1 or better. + - Support for REPEATABLE READ isolation level added. + - Makefile fixes for Visual Studio 2013. + - Supports C++11 and C++14. + - No longer has obsolete debian & RPM packaging built in. + - Fixed failure to abort uncommitted subtransactions on destruction. + - Fixed failure to detect some integer overflows during conversion. + - Build tooling uses /usr/bin/env python instead of /usr/bin/python. + - New configure options: --with-postgres-include and --with-postgres-lib. + - In g++ or compatible compilers, non-exported items are no longer accessible. + - Many build fixes for various platforms and compilers. +4.0 + - API change: noticers are gone! Use errorhandlers to capture error output. + - API change: tablereaders and tablewriters are gone; they weren't safe. + - API change: prepared statements are now weakly-typed, and much simpler. + - API change: fields and tuples are now stand-alone classes in ::pqxx. + - API change: thread-safety field have_strerror_r is now have_safe_strerror. + - API change: notify_listener has been replaced with notification_receiver. + - notification_receiver takes a payload parameter. + - Easier Visual C++ setup. + - Absolutely requires a libpq version with PQescapeStringConn. + - Absolutely requires libpq 8.0 or better. + - Changes for C++0x. + - Supports clang++. + - Visual C++ makefiles now support new-style unit tests. + - Sample headers for more recent Visual Studio versions. + - Fixes binary-data escaping problems with postgres 9.0. + - Fixes problems with binary-string handling and escaping. + - Fixes compatibility problems between 9.x libpq and 7.x backend. + - quote_name to escape SQL identifiers for use in queries. + - syntax_error reports error's approximate location in the query. + - On Windows, now uses ws2_32 instead of wsock32. + - Various Windows build fixes. + - Updated for gcc 4.6.0. + - configure script supports --enable-documentation/--disable-documentation. + - Streamlined test/release toolchain. +3.1 + - Shared libraries are now versioned by ABI: 3.1 instead of 3.1.0 etc. + - Threading behaviour is now documented, and can be queried. + - Version information available at compile time. + - Supports parameterized statements. + - Result tuples now support slicing. + - Configure with --with-tr1=boost to use BOOST shared_ptr. + - String conversion now has its own header file. + - Supports read-only transactions. + - Fixed breakage with Solaris "make". + - Uses shared_ptr if available. + - binarystring::str() is no longer cached; no longer returns reference. + - Fixed problems in Visual C++ Makefile for test suite. + - Fixed problems with RPM packaging. + - Fixed build problem on RedHat/CentOS 5. + - Lets you check whether a prepared statement has been defined. + - "Varargs" prepared statements. + - Unnamed prepared statements now supported. + - Results have iterator as well as const_iterator. + - Rewrite of robusttransaction logic; may actually do its job now. + - Connections support async query cancel from signal handler or thread. + - More documentation for performance features. +3.0 + - Website is now at http://pqxx.org/ (no redirects) + - Completely replaced cursor classes + - More helpful error messages on failed connections + - More detailed hierarchy of constraint-violation exception classes + - trigger is now called notify_listener, trigger header is now notify-listen + - New mixin base class pqxx_exception distinguishes libpqxx exception types + - Quoting is back! transaction_base::quote() & connection_base::quote() + - Several build & documentation problems with Visual C++ fixed + - Compile fixes for gcc 4.2, 4.3 + - Compile fixes for Sun Studio Express 5.9 + - Uses strlcpy() where available, instead of strncpy() + - Keeps better track of applicable text encodings + - Fixed bug with prepared statement parameters in separate C++ statements + - robusttransaction now works for multiple users + - Pipeline lets you cancel ongoing queries, e.g. because they run for too long + - Fixed broken escaping of binary values in tablewriter + - Floating-point types now represented with full precision + - Proper unit tests for new functionality + - New traits-based system for adding data types + - Floating-point infinities now supported + - Flushing/completing a pipeline now frees up the transaction for other use + - Completely reworked test suite, builds and runs much faster + - tablewriter supports writing of raw lines +2.6.9 + - Removed old 1.x API (that means all identifiers with capital letters!) + - Tested with all current libpq versions and oldest/newest supported backends + - No longer have old OnCommit()/OnAbort()/OnDoubt() callbacks in transactor! + - Fixes failure when closing cursors with upper-case letters in their names + - Fixes bug when adding triggers to connections that aren't open yet + - Fixes bug when removing triggers + - Fixes small memory leak when preparing statements + - Fixes many problems with older backends + - Fixes bug in result::swap(): protocol versions were not swapped + - Some errors went undetected when using certain libpq versions + - Fixes prepared statements on new libpq versions talking to old backends + - Can estimate server version if libpq does not know how to obtain it + - Greatly reduced memory usage while escaping strings + - With Visual C++, creates lib/ directory if not already present + - Useful error messages when preparing statements + - Allows prepared statements to be registered explicitly + - Support for "long long" types; enable with PQXX_ALLOW_LONG_LONG macro + - Compilation errors for older libpq versions fixed + - Some new small utility classes for disabling notice processing etc. + - Result sets remember the queries that yielded them + - New test script, pqxx-fulltest, tests against all current postgres versions + - Connections can simulate failure + - Adds password encryption function +2.6.8 + - Fixes bug: binary parameters to prepared statements truncated at nul bytes + - New, more specific exception types to distinguish errors from server + - Resolved serious problems with generated reference documentation + - Automatically detect Windows socket library with MinGW + - Windows "make" fixed to run from main directory, not win32 + - Fixes "mktemp" problems on some BSD-based platforms + - pqxx-config is deprecated; use pkg-config instead + - On GNU/Linux, uses poll() instead of select() to avoid file descriptor limit + - Will provide server and protocol version information where available + - New cursor class, absolute_cursor +2.6.7 + - New escape functions for binary data: transaction_base::esc_raw() + - Improved detection of socket libraries, especially for MinGW + - Works around bug in some versions of GNU grep 2.5.1 + - Fixes problem with configuration headers + - Fixes PQprepare() detection + - Fixes incomplete Visual C++ Makefile + - Fixes compile error in workaround for older libpq versions + - Removes "rpath" link option +2.6.6 + - New, encoding-safe string-escaping functions + - Upper-case letters now allowed in prepared-statement names + - Fixes crash in test005 + - More Visual C++ improvements + - Removed collaboration diagrams from reference docs + - New templating system for generating Windows Makefiles etc. +2.6.5 + - Visual C++ users: copy win32/common-sample to win32/common before editing it + - Should fix problems finding socket library on MinGW + - Even more work on Visual C++ problems + - Updated documentation for Visual C++ users + - Fixed bug in prepared statements (mostly visible on Visual C++) + - Nested transactions work harder to detect backend support +2.6.4 + - Massively improved compatibility with Windows and Visual C++ + - Fixed late initialization of "direct" connection state + - Fixed problem with initialization of connection capabilities + - Fixed configuration bug for libpq in nonstandard locations + - Sample configuration header for libpq found in PostgreSQL 8.1 +2.6.3 + - Radical rework of prepared statements; INCOMPATIBLE INTERFACE CHANGE! + - Dropped support for g++ 2.95 + - Emulate prepared statements support on old libpq or old backend + - Bug fix: missing tutorial (release script now tests for this) + - Automatically links in socket library on Windows or Solaris, if needed + - Bug fix: check for std namespace didn't work + - Fixes for Cygwin/MSYS/MinGW +2.6.2 + - Bug fix: connection state was not set up properly in some common cases + - Bug fix: headers were installed in "include" instead of "include/pqxx" + - Bug fix: sqlesc(string) broke with multibyte or multiple encodings + - namedclass is now used as a virtual base; affects all subclass constructors + - Initial implementation of subtransactions + - Detect more connection capabilities + - Standard library namespace can be set from configure script's command line + - Completely reworked connection hierarchy, with separate policy objects + - Clients can now define their own connection policies + - Paved the way for client-defined thread synchronization + - Now lives at http://thaiopensource.org/development/libpqxx/ +2.6.1 + - Hugely improved recognition of different strerror_r() versions + - Resolved link problems with gcc 4.0 and shared library +2.6.0 + - New macro PQXX_SHARED defines whether to use/build libpqxx as shared library + - Robusttransaction compatible with PostgreSQL 8.1 + - Infrastructure for querying connection/backend capabilities at runtime + - Greatly improved cursor support + - Connection reactivation can be inhibited explicitly + - Tries even harder to make sense of conflicting strerror_r() definitions + - Detects connection failures that libpq glosses over + - Reference documentation grouped into more coherent sections + - Assumes strerror() is threadsafe on systems that have no strerror_r() + - Now allows connection's socket number to be queried + - New internal_error class for libpqxx-internal errors + - With Visual C++, doesn't redefine NOMINMAX if it is defined already + - Several compatibility improvements for Visual C++ + - Fixes and workarounds for HP-UX and HP aCC compiler + - Phased old cursor interface out of test suite; tests ported to new interface + - Added documentation on thread safety + - New thread safety model + - Large objects have functions to tell current position + - Minor updates to tutorial (somebody pay me and I'll do more :) + - No longer needs libpq-fs.h header + - Meaningful error messages for ambiguous string conversions fixed +2.5.6 + - Support null parameters to prepared statements (use C-style char pointers) +2.5.5 + - Diagnoses connection failure during result transfer + - Fixes invalid -R link option in pqxx-config +2.5.4 + - Fix workaround code for older libpq versions without PQunescapeBytea() + - Work around grep bug in Fedora Core 4 that broke configure in UTF-8 locales + - In Visual C++, assume libpqxx is a DLL when linking to std library as DLL + - Missing documentation in distribution archive is back again + - Export fewer symbols from library binary with gcc 4.0 + - Releases now automatically tested against gcc 4.0 + - Meaningful link errors for additional ambiguous string conversions + - DLL symbol exports now automatically tested before each release +2.5.3 + - Greatly improved builds on MinGW with MSYS + - All known problems with MinGW fixed + - Fix bugs in stream classes that caused failures and crashes with STLport + - Detects and uses STLport automatically +2.5.2 + - Fix memory leaks + - Fix problems with NaN (not-a-number values) on some compilers +2.5.1 + - Fix configure script; broke when very recent libpqxx was already installed + - Fix cursor breakage when "long" is more than 32 bits + - Fix cases where new-style abort/doubt handlers are used + - Fix for division-by-zero error in Visual C++ (changed sample headers) + - Improved checking for strerror_r in configure script + - Fix for problem MinGW has with configure script + - Fix spurious failure of Oid check in configure script +2.5.0 + - Fix race condition in removing triggers + - Fix binary string conversion with older libpq + - Fix some error strings that may previously have come out wrong + - No longer includes any libpq headers while compiling client code + - Improved thread safety: avoid strerror() where possible + - Prepared statements + - Translate more error conditions to std::bad_alloc exception + - Clearer and more specific explanations for configuration failures + - Improved documentation + - Looks for standard library in global namespace as well as std + - Accepts standard C library in std namespace + - Release script automatically tests with a range of compilers, not just one + - Compatible with g++ 2.95 again; this time it's tested automatically +2.4.4 + - Fix problems building shared library in Visual C++ + - Fix autobuild in Debian, which was broken by mistake in BSD grep workaround + - Fix conversion of string to floating-point type NaN + - Remove stray CVS directories from distribution archive + - Workaround for Visual C++ problem when issuing messages from destructors + - Yet more workarounds for Visual C++ bugs + - Fix situation where connection state might not be restored after failure + - Fix configuration problem on SunOS + - Network speedup in connection setup with pending variables and/or triggers +2.4.3 + - Yet more workarounds for bugs in Visual C++ .NET 2003 + - Fixes for SunC++ 5.5 + - On Visual C++, now defines NOMINMAX, fixing large object support + - Workaround for BSD grep + - Improvements for builds from CVS + - Sample config headers for Sun ONE Studio 8 +2.4.2 + - Fix minor problems with Apple's version of g++ 3.3 + - Fix problem with MingW on Windows + - Workarounds and fixes for Visual C++.NET 2003 + - Renewed compatibility with g++ 2.95 + - More sample configuration headers + - Updated reference documentation + - Removed assert code +2.4.1 + - Several bugs in icursor_iterator fixed; incompatible interface changes + - Tightens throw specifications on begin(), end(), size(), capacity() + - Containers define reference and pointer types + - Implements swap() in all container types + - Implements == and != in all container types + - Stabilizes new (but still limited) cursor interface + - icursor_iterator thinks purely in stride granularity + - Introduces />= comparisons for icursor_iterators + - Allows "adopted SQL cursors" in new cursor interface + - Reference-counting in binarystrings, so they can be copied (and efficiently) + - Fixes reference-to-temporary problem with std::reverse_iterator in results + - Result/tuple reverse_iterators no longer require std::reverse_iterator + - Includes some sample config headers (in config/sample-headers) + - Replaces iffy autoconf checks (avoid failures with maintainer mode's -Werror) + - Fixes incompatibility with some implementations of Unix "cut" program (again) +2.4.0 + - Fixes incompatibility with some implementations of Unix "cut" program + - Fixes "ptrdiff_t redefinition" problem in some environments + - More container-like tuples, so fields can be iterated + - All size_type types are now unsigned + - More conservative robusttransaction--thanks Tom Lane + - Stream-like extraction operator for result field conversion + - Warnings about deprecated headers now suppressed while compiling library + - Iterator constructors and copy assignments now have empty throw specs +2.3.0 + - Generates MinGW Makefile automatically + - Documents MinGW build + - Workaround for missing prepared-statement support + - Potential bug fixed in closing of connections + - Fixed incompatibility between new cursor streams and older backends + - Removed pqxxbench +2.2.9 + - Bugfix in removing trigger + - Added "failed connection" to regression test + - Some changes to throw specifications + - Putting libpq in its own namespace is optional +2.2.8 + - Moved libpq into pqxx::internal::pq namespace + - New config system separates compiler-related items from libpq-related ones + - Auto-generates Visual C++ Makefile, should always remain up-to-date now +2.2.7 + - Bugfix: from_string() didn't handle LONG_MIN--thanks Yannick Boivin +2.2.6 + - Complete "pipeline" rewrite, for better exception safety + - New garbage collection scheme for "result;" constructors now exception-free +2.2.5 + - First new cursor classes! + - Fixed strange failure in tablewriter during large insertions + - Updated tutorial +2.2.4 + - New utility class template, items<> for easy container initialization + - New utility function template, separated_list() + - Error handling bugfix in tablewriter + - Fixed tablereader handling of lines ending in empty fields + - tablereader lines no longer end in newline with old libpq versions +2.2.3 + - Trigger names no longer need to be proper identifiers + - Compile fixes for g++ 3.4.0 and other modern compilers + - Tablestreams may specify column lists + - Deprecated Quote() in favour of sqlesc(); improved quoting + - Fixed generation of libpqxx.spec +2.2.2 + - Bugfix in fieldstream w.r.t. reading strings on some systems + - Renamed config.h to internalconfig.h to avoid confusion + - New connection functions allow client to sleep until notification arrives + - Notification functions return number of notifications received + - Even fewer client-visible macros exported by libconfig.h +2.2.1 + - New, 2.x-style string conversions without locale problem + - Documentation improvements + - Implemented result::swap() +2.2.0 + - Installs to /usr/local by default, NOT to /usr/local/pqxx like before! + - Uses Postgres-provided script to find Postgres (thanks Peter Eisentraut) + - Which means no more configure arguments required on Irix (thanks Arjen Baart) + - Fixes long-standing bug in result class! + - New pipeline class for throughput optimization + - New field stream class: read result field as C++ stream + - Separate namespace pqxx::internal for definitions not relevant to the user + - More Windows compilation fixes + - SUN Workshop 6 compile fixes and workarounds (thanks Jon Meinecke) + - Implemented reverse_iterator for result class + - Checks for functional std::reverse_iterator template + - Preliminary Makefile for MinGW compiler (thanks Pasquale Fersini) + - Changed the way unique<> works + - Checks for functional std::count_if() + - Bugs fixed & test programs added +2.1.3 + - Makefile fixes for Visual C++, thanks Paresh Patel + - Library ABI versioning implemented, thanks Roger Leigh + - Uses old SQL isolation level syntax for compatibility, thanks koun@sina.com + - tablestreams can explicitly complete() before destructor + - Bugfix in robusttransaction: forgot to set isolation level + - Fixed off-by-ones in tablewriter escape code + - tablestreams now use \n-style escape sequences + - tablestreams support octal numbers + - Freely definable "null" strings in tablestreams, as originally intended + - Improved Debian packaging, thanks Roger Leigh + - tablestreams use libpq's new-style COPY functions, if available + - Extended automation of build/release procedure + - tablewriter writes in nonblocking mode to help hide communication latency + - Can get backend variables as well as set them + - More configuration macro cleanups + - Workaround for missing clear() in standard string + - Merry Christmas! +2.1.2 + - Compile fix for gcc libstdc++ 2.9, thanks Jaroslaw Staniek + - Moved deprecated functions below current ones + - Cleanups for Debian packaging (thanks Roger Leigh, new Debian maintainer!) + - Updated authors listings + - Bumped ABI version number for the first time (now 2:0:1) +2.1.1 + - More workarounds for gcc 2.95 + - Automated tools keep test makefiles up to date +2.1.0 + - Asynchronous connections + - Fixed configure --includedir option (thanks Ray Dassen!) + - Compile fixes for SUN Workshop 6, and one for gcc on FreeBSD 4.8 +2.0.0 + - New stable release! + - Includes all changes since 1.5 release. + - Workarounds for Microsoft Visual C++ 7 problems. Thanks Costin Musteata! + - No longer need to define PQXX_NO_PARTIAL_CLASS_TEMPLATE_SPECIALISATION + - Integrated Windows configuration into regular configuration + - Only uses #warning if preprocessor supports it + - Works on libpq versions without PQ[un]escapeBytea() +1.9.9 + - Minor documentation changes +1.9.8 + - Workaround for compile problem with postgres 7.3 + - Convenience typedef for transaction<>: "work" +1.9.7 + - binarystring rewritten and moved to its own file + - binarystring::size() does not include terminating null byte! + - Implemented escaping of binary strings + - Fix in workaround for missing numeric_limits on some compilers + - String conversion supported for unsigned char * + - More helpful link errors for unsupported string conversions + - Complete test coverage +1.9.6 + - Fixes in "field table" support + - Improved coexistence with client program's config.h, if any + - Prefixed autoconf macros used in headers with "PQXX_" +1.9.5 + - Header file contents moved to .hxx files for editor filetype recognition + - Fixes wrong timestamp for include/pqxx/result in 1.9.4 distribution +1.9.4 + - Fixes Visual C++ build problem when compiling as library +1.9.3 + - Quick release for various minor changes +1.9.2 + - Renamed most public member functions to all-lower-case names + - (previously is now called +1.9.1 + - tablestream destructor crashed if table didn't exist (thanks Sean [Rogers?]) + - Renamed all header files to remove ".h" suffix + - Tables created by regression test now prefixed with "pqxx" for safety + - Large objects now considered stable + - Migrated tutorial from SGML to DocBook XML (thanks Wichert Akkerman) + - Added tests 57-59 + - Fixed compile error in largeobject + - Updated Windows makefiles +1.9.0 + - EVERYTHING HAS CHANGED. Read the list or run into trouble! + - CURSOR HAS INCOMPATIBLE CHANGES AND MAY BE REPLACED COMPLETELY + - CACHEDRESULT HAS INCOMPATIBLE CHANGES (won't compile without changes) + - REVISE YOUR TRANSACTORS; now templatized on transaction type + - Finally got license file in order + - Incompatible change in setting transactor quality of service + - Cursors require serializable isolation level (checked at link time) + - Renamed Connection_base to connection_base, Connection to connection, + LazyConnection to lazyconnection + - Renamed LargeObject to largeobject, LargeObjectAccess to largeobjectaccess + - Renamed Noticer to noticer + - Renamed Trigger to trigger + - Renamed Result to result, Tuple to tuple, Field to field + - Renamed Unique<> to unique<> + - Renamed CachedResult to cachedresult + - Transformed Transaction Taxonomy (TTT): + - Renamed Transaction_base to transaction_base + - Renamed Transaction to transaction + - Renamed Transactor to transactor<> (now a template) + - Implemented transaction isolation levels as compile-time static properties + - transaction and robusttransaction now templatized on their isolation levels + - cachedresult requires serializable isolation level (checked at link time) + - Now need to include pqxx/transactor.h yourself if you need transactors + - Large objects require real backend transaction at compile time + - New type oid and constant oid_none for row identifiers resp. null oid + - Added some forgotten PQXX_LIBEXPORTs + - Tweaked documentation in many places +1.8.1 + - By popular request: more convenient way to read field values + - Documented locale sensitivity of ToString(), FromString(), Field::to() +1.8.0 + - Compiles on gcc 2.95 again (heavy streambuf workarounds in largeobject.h) + - ConnectionItf renamed to Connection_base, TransactionItf to Transaction_base + - connectionitf.h is now connection_base.h, transactionitf.h connection_base.h +1.7.8 + - BinaryString class for unescaping bytea strings + - PQAlloc template keeps track of libpq-allocated objects + - Removed some consts in Unique<>, ConnectionItf, sorry! + - Can now set session variables on connections, transactions +1.7.7 + - ./configure also looks for postgres in /usr/local/pgsql + - test007 now uses SQL_ASCII as its test encoding + - integrated Greg Hookey's Debian packaging +1.7.6 + - added postgres library (libpq) to dynamic link path +1.7.5 + - added test052 - test055 + - added Result::Tuple::ColumnNumber() + - also test setting of client encodings + - removed superfluous versions of to_file() from large object classes +1.7.4 + - new exception class, sql_error, remembers query text + - moved exception classes to new file include/pqxx/except.h + - test cases report texts of any failed queries + - added tools/rmlo.cxx +1.7.3 + - default constructors for connection classes + - revamped seeking operations on large objects + - better error messages in large objects + - added test050, test051 +1.7.2 + - more workarounds for Sun CC 5.1, thanks Jeroen van Erp! + - preliminary support for "named" queries + - can now Quote() string constants + - included Doxyfile in distribution archive + - helps avoid Windows memory allocation problem in DLLs + - allows setting of client character set encoding +1.7.1 + - regenerated documentation +1.7.0 + - removed all deprecated features + - connection string documentation in README + - separate Connection, LazyConnection classes + - made test001 more concise + - added test049 +1.6.4 + - configure script now respects different std namespace +1.6.3 + - olostream, lostream now flush themselves before closing + - fixed compilation problems when using ToString<>() on a plain char * + - compilation fixes for Sun compiler (thanks Jeroen van Erp!) + - added .pc file for pkgconfig (thanks Ray Dassen!) +1.6.2 + - Debian packaging added to distribution archive + - new ilostream, olostream, lostream classes +1.6.1 + - large object's cunlink() replaced by remove() + - default constructor for LargeObject +1.6.0 + - new large objects interface + - added test048 +1.5.0 + - allow result fields to be written to streams + - removed confusing CachedResult::clear() + - minor documentation updates + - added test046, test047 + - added convenience header +1.4.5 + - fixed crash CachedResult that was less shallow than I thought + - fixed quoting problem with adopted SQL cursors +1.4.4 + - (forgot to save cursor.cxx with new constructor in 1.4.4, sorry) +1.4.3 + - all tests now have three-digit numbers + - Cursor can adopt SQL cursor returned by a function +1.4.2 + - bugfix in CachedResult when accessing empty Results + - minor documentation improvements +1.4.1 + - documents new homepage: http://pqxx.tk/ + - Connection constructor accepts null connect string + - Exec() now also takes queries as C++ strings +1.4.0 + - Connection::IsOpen() renamed to is_open() + - NoticeProcessor replaced by Noticer (with C++ linkage) +1.3.7: + - detects nasty rare problem case with Cursors in unknown positions +1.3.6: + - fixed detection of missing PQescapeString(). Thanks David Wright! +v1.3.5: + - documented Windows build procedure + - fixed problem with upper-case letters in cursor names. Thanks key88! +2003-01-19 16:00, v1.3.4: + - support long double type + - clarified some error messages +2003-01-08 18:45, v1.3.3: + - fix missing include in test13 +2003-01-07 02:30, v1.3.2: + - configure looks for postgres includes/library in more places, thanks Ray! +2003-01-02 23:00, v1.3.1: + - bugfix in Cursor positioning +2003-01-02 20:30, v1.3.0: + - absolute positioning for Cursor + - better documentation on cursors + - reduced, but improved test suite output +2002-12-23 17:30, v1.2.8: + - Cursor::Move() returns number of rows skipped + - new typedef Cursor::size_type +2002-12-14 23:30, v1.2.7: + - test suite now distinguishes expected errors from unexpected ones +2002-12-09 20:00, v1.2.6: + - fixed some Cursor test cases for change in postgres 7.3 + - added important warning to Cursor +2002-12-09 02:00, v1.2.5: + - added important warning to CachedResult +2002-12-08 14:14, v1.2.4: + - fixed compile error on some systems in include/pqxx/util.h +2002-12-04 12:00, v1.2.3: + - workaround for broken on some systems + - fixed Quote() bug +2002-12-03 01:30, v1.2.2: + - fixed serious CachedResult bug + - added test41 +2002-12-02 17:00, v1.2.1: + - hopefully fixed cursor bug with PostgreSQL 7.3 +2002-12-01 22:00, v1.2.0: + - new CachedResult class +2002-11-07 13:15, v1.1.4: + - workaround for missing InvalidOid definition +2002-10-23 16:00, v1.1.3: + - Cursor & TableStream hierarchy now work on any transaction type + - get no. of affected rows & oid of inserted row from Result + - increased test coverage +2002-10-21 01:30, v1.1.2: + - updated build procedure + - Debian packaging improvements +2002-09-25 03:00, v1.1.1: + - supports activating/deactivating of connections + - various Connection getters now activate deferred connection first +2002-09-23 01:00, v1.1.0: + - supports lazy connections (added 19 test cases just for these) + - greatly reduced performance overhead for RobustTransaction + - removed id field from RobustTransaction's transaction log tables +2002-09-14 20:00, v1.0.1: + - now lives on GBorg + - various packaging updates +2002-06-12 17:30, v0.5.1: + - no longer have to destroy one transaction before creating the next +2002-06-07 17:15, v0.5.0: + - "make install" now finally installs headers! + - distribution now includes SGML (DocBook) version of tutorial +2002-06-04 15:00, v0.4.4: + - may now have multiple triggers with same name on single connection +2002-06-02 23:00, v0.4.3: + - fixed TableReader problem with \t and \n +2002-06-01 21:00, v0.4.2: + - hopefully fixes compile problem with broken std::iterator + - configure no longer requires --with-postgres-include=/usr/include/postgresql +2002-05-29 22:00, v0.4.1: + - can now also handle bool, unsigned char, short field types +2002-05-27 22:30, v0.4.0: + - RENAMED Transactor::TRANSACTIONTYPE to argument_type for STL conformance + - RENAMED Result::Field::name() to Name() + - documentation improvements + - minor optimizations +2002-05-18 00:00, v0.3.1: + - removed broken postgres_fe.h dependency (hopefully permanent fix) +2002-05-12 22:45, v0.3.0: + - also looks for postgres_fe.h in postgres' internal/ directory (tmp fix) +2002-05-05 01:30, v0.2.3: + - extensive build instructions in README + - make check now controlled through PG environment variables +2002-05-04 19:30, v0.2.2: + - more STL conformance + - fixed regression test + - test6 now copies "orgevents" to "events" by default +2002-04-28 23:45 Version bumped to 0.2 +2002-04-28 23:45 Self-generated distribution archive +2002-04-27 14:20 Replaced automake symlinks with actual files +2002-04-07 02:30 Released with configure script +2002-03-29 01:15 Not yet released. Still integrating autogen stuff... diff --git a/ext/libpqxx-7.7.3/README.md b/ext/libpqxx-7.7.3/README.md new file mode 100644 index 000000000..3a1c17ded --- /dev/null +++ b/ext/libpqxx-7.7.3/README.md @@ -0,0 +1,199 @@ +libpqxx +======= + +Welcome to libpqxx, the C++ API to the PostgreSQL database management system. + +Home page: http://pqxx.org/development/libpqxx/ + +Find libpqxx on Github: https://github.com/jtv/libpqxx + +Documentation on Read The Docs: https://libpqxx.readthedocs.io + +Compiling this package requires PostgreSQL to be installed -- or at least the C +headers and library for client development. The library builds on top of +PostgreSQL's standard C API, libpq, though your code won't notice. + +If you're getting the code straight from the Git repo, the head of the `master` +branch represents the current _development version._ Releases are tags on +commits in `master`. For example, to get version 7.1.1: + + git checkout 7.1.1 + + +Upgrade notes +------------- + +**The 7.x versions require at least C++17.** Make sure your compiler is up to +date. For libpqxx 8.x you will need at least C++20. + +Also, **7.0 makes some breaking changes in rarely used APIs:** +* There is just a single `connection` class. It connects immediately. +* Custom `connection` classes are no longer supported. +* It's no longer possible to reactivate a connection once it's been closed. +* The API for defining string conversions has changed. + +If you're defining your own type conversions, **7.1 requires one additional +field in your `nullness` traits.** + + +Building libpqxx +---------------- + +There are two different ways of building libpqxx from the command line: +1. Using CMake, on any system which supports it. +2. On Unix-like systems, using a `configure` script. + +"Unix-like" systems include GNU/Linux, Apple macOS and the BSD family, AIX, +HP-UX, Irix, Solaris, etc. Even on Microsoft Windows, a Unix-like environment +such as WSL, Cygwin, or MinGW should work. + +You'll find detailed build and install instructions in `BUILDING-configure.md` +and `BUILDING-cmake.md`, respectively. + +And if you're working with Microsoft Visual Studio, have a look at Gordon +Elliott's +[ + Easy-PQXX Build for Windows Visual Studio +](https://github.com/GordonLElliott/Easy-PQXX-Build-for-Windows-Visual-Studio) +project. + + +Documentation +------------- + +Building the library, if you have the right tools installed, generates HTML +documentation in the `doc/` directory. It is based on the headers in +`include/pqxx/` and text in `include/pqxx/doc/`. This documentation is also +available online at [readthedocs](https://libpqxx.readthedocs.io). + + +Programming with libpqxx +------------------------ + +Your first program will involve the libpqxx classes `connection` (see the +`pqxx/connection.hxx` header), and `work` (a convenience alias for +`transaction<>` which conforms to the interface defined in +`pqxx/transaction_base.hxx`). + +These `*.hxx` headers are not the ones you include in your program. Instead, +include the versions without filename suffix (e.g. `pqxx/connection`). Those +will include the actual .hxx files for you. This was done so that includes are +in standard C++ style (as in `` etc.), but an editor will still +recognize them as files containing C++ code. + +Continuing the list of classes, you will most likely also need the result class +(`pqxx/result.hxx`). In a nutshell, you create a `connection` based on a +Postgres connection string (see below), create a `work` in the context of that +connection, and run one or more queries on the work which return `result` +objects. The results are containers of rows of data, each of which you can +treat as an array of strings: one for each field in the row. It's that simple. + +Here is a simple example program to get you going, with full error handling: + +```c++ + #include + #include + + int main() + { + try + { + // Connect to the database. + pqxx::connection C; + std::cout << "Connected to " << C.dbname() << '\n'; + + // Start a transaction. + pqxx::work W{C}; + + // Perform a query and retrieve all results. + pqxx::result R{W.exec("SELECT name FROM employee")}; + + // Iterate over results. + std::cout << "Found " << R.size() << "employees:\n"; + for (auto row: R) + std::cout << row[0].c_str() << '\n'; + + // Perform a query and check that it returns no result. + std::cout << "Doubling all employees' salaries...\n"; + W.exec0("UPDATE employee SET salary = salary*2"); + + // Commit the transaction. + std::cout << "Making changes definite: "; + W.commit(); + std::cout << "OK.\n"; + } + catch (std::exception const &e) + { + std::cerr << e.what() << '\n'; + return 1; + } + return 0; + } +``` + + +Connection strings +------------------ + +Postgres connection strings state which database server you wish to connect to, +under which username, using which password, and so on. Their format is defined +in the documentation for libpq, the C client interface for PostgreSQL. +Alternatively, these values may be defined by setting certain environment +variables as documented in e.g. the manual for psql, the command line interface +to PostgreSQL. Again the definitions are the same for libpqxx-based programs. + +The connection strings and variables are not fully and definitively documented +here; this document will tell you just enough to get going. Check the +PostgreSQL documentation for authoritative information. + +The connection string consists of attribute=value pairs separated by spaces, +e.g. "user=john password=1x2y3z4". The valid attributes include: +* `host` — + Name of server to connect to, or the full file path (beginning with a + slash) to a Unix-domain socket on the local machine. Defaults to + "/tmp". Equivalent to (but overrides) environment variable PGHOST. +* `hostaddr` — + IP address of a server to connect to; mutually exclusive with "host". +* `port` — + Port number at the server host to connect to, or socket file name + extension for Unix-domain connections. Equivalent to (but overrides) + environment variable PGPORT. +* `dbname` — + Name of the database to connect to. A single server may host multiple + databases. Defaults to the same name as the current user's name. + Equivalent to (but overrides) environment variable PGDATABASE. +* `user` — + User name to connect under. This defaults to the name of the current + user, although PostgreSQL users are not necessarily the same thing as + system users. +* `requiressl` — + If set to 1, demands an encrypted SSL connection (and fails if no SSL + connection can be created). + +Settings in the connection strings override the environment variables, which in +turn override the default, on a variable-by-variable basis. You only need to +define those variables that require non-default values. + + +Linking with libpqxx +-------------------- + +To link your final program, make sure you link to both the C-level libpq library +and the actual C++ library, libpqxx. With most Unix-style compilers, you'd do +this using the options + + -lpqxx -lpq + +while linking. Both libraries must be in your link path, so the linker knows +where to find them. Any dynamic libraries you use must also be in a place +where the loader can find them when loading your program at runtime. + +Some users have reported problems using the above syntax, however, particularly +when multiple versions of libpqxx are partially or incorrectly installed on the +system. If you get massive link errors, try removing the "-lpqxx" argument from +the command line and replacing it with the name of the libpqxx library binary +instead. That's typically libpqxx.a, but you'll have to add the path to its +location as well, e.g. /usr/local/pqxx/lib/libpqxx.a. This will ensure that the +linker will use that exact version of the library rather than one found +elsewhere on the system, and eliminate worries about the exact right version of +the library being installed with your program.. diff --git a/ext/libpqxx-7.7.3/VERSION b/ext/libpqxx-7.7.3/VERSION new file mode 100644 index 000000000..d92ba80de --- /dev/null +++ b/ext/libpqxx-7.7.3/VERSION @@ -0,0 +1 @@ +7.7.3 diff --git a/ext/libpqxx-7.7.3/aclocal.m4 b/ext/libpqxx-7.7.3/aclocal.m4 new file mode 100644 index 000000000..2c938cff6 --- /dev/null +++ b/ext/libpqxx-7.7.3/aclocal.m4 @@ -0,0 +1,1187 @@ +# generated automatically by aclocal 1.16.4 -*- Autoconf -*- + +# Copyright (C) 1996-2021 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# Copyright (C) 2002-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.16' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.16.4], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.16.4])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + AS_CASE([$CONFIG_FILES], + [*\'*], [eval set x "$CONFIG_FILES"], + [*], [set x $CONFIG_FILES]) + shift + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf + do + # Strip MF so we end up with the name of the file. + am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`AS_DIRNAME(["$am_mf"])` + am_filepart=`AS_BASENAME(["$am_mf"])` + AM_RUN_LOG([cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles]) || am_rc=$? + done + if test $am_rc -ne 0; then + AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. If GNU make was not used, consider + re-running the configure script with MAKE="gmake" (or whatever is + necessary). You can also try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking).]) + fi + AS_UNSET([am_dirpart]) + AS_UNSET([am_filepart]) + AS_UNSET([am_mf]) + AS_UNSET([am_rc]) + rm -f conftest-deps.mk +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking is enabled. +# This creates each '.Po' and '.Plo' makefile fragment that we'll need in +# order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +# Variables for tags utilities; see am/tags.am +if test -z "$CTAGS"; then + CTAGS=ctags +fi +AC_SUBST([CTAGS]) +if test -z "$ETAGS"; then + ETAGS=etags +fi +AC_SUBST([ETAGS]) +if test -z "$CSCOPE"; then + CSCOPE=cscope +fi +AC_SUBST([CSCOPE]) + +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +dnl The trailing newline in this macro's definition is deliberate, for +dnl backward compatibility and to allow trailing 'dnl'-style comments +dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- +# From Jim Meyering + +# Copyright (C) 1996-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAINTAINER_MODE([DEFAULT-MODE]) +# ---------------------------------- +# Control maintainer-specific portions of Makefiles. +# Default is to disable them, unless 'enable' is passed literally. +# For symmetry, 'disable' may be passed as well. Anyway, the user +# can override the default with the --enable/--disable switch. +AC_DEFUN([AM_MAINTAINER_MODE], +[m4_case(m4_default([$1], [disable]), + [enable], [m4_define([am_maintainer_other], [disable])], + [disable], [m4_define([am_maintainer_other], [enable])], + [m4_define([am_maintainer_other], [enable]) + m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) +AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode's default is 'disable' unless 'enable' is passed + AC_ARG_ENABLE([maintainer-mode], + [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], + am_maintainer_other[ make rules and dependencies not useful + (and sometimes confusing) to the casual installer])], + [USE_MAINTAINER_MODE=$enableval], + [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST([MAINT])dnl +] +) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check whether make has an 'include' directive that can support all +# the idioms we need for our automatic dependency tracking code. +AC_DEFUN([AM_MAKE_INCLUDE], +[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) +cat > confinc.mk << 'END' +am__doit: + @echo this is the am__doit target >confinc.out +.PHONY: am__doit +END +am__include="#" +am__quote= +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) + AS_CASE([$?:`cat confinc.out 2>/dev/null`], + ['0:this is the am__doit target'], + [AS_CASE([$s], + [BSD], [am__include='.include' am__quote='"'], + [am__include='include' am__quote=''])]) + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +AC_MSG_RESULT([${_am_result}]) +AC_SUBST([am__include])]) +AC_SUBST([am__quote])]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + MISSING="\${SHELL} '$am_aux_dir/missing'" +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 2001-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2021 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([config/m4/libtool.m4]) +m4_include([config/m4/ltoptions.m4]) +m4_include([config/m4/ltsugar.m4]) +m4_include([config/m4/ltversion.m4]) +m4_include([config/m4/lt~obsolete.m4]) diff --git a/ext/libpqxx-7.7.3/appveyor.yml b/ext/libpqxx-7.7.3/appveyor.yml new file mode 100644 index 000000000..110bd75ad --- /dev/null +++ b/ext/libpqxx-7.7.3/appveyor.yml @@ -0,0 +1,30 @@ +# Configuration for test runs in Appveyor. +version: 1.0.{build} +image: Visual Studio 2022 +services: postgresql13 +# Run CMake to build libpqxx.sln. +before_build: +- cmd: >- + call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" + + cmake -DBUILD_SHARED_LIBS=1 -DCMAKE_CXX_STANDARD=23 +configuration: Release +build: + parallel: true + project: libpqxx.sln +test_script: +- ps: >- + $env:Path += ";.\src\Release;C:\Program Files\PostgreSQL\13\bin" + + $env:PGUSER = "postgres" + + $env:PGPASSWORD = "Password12!" + + .\test\Release\runner.exe +notifications: +- provider: Email + subject: 'libpqxx: AppVeyor build failure' + message: The libpqxx AppVeyor build has failed. + on_build_success: false + on_build_failure: true + on_build_status_changed: false diff --git a/ext/libpqxx-7.7.3/autogen.sh b/ext/libpqxx-7.7.3/autogen.sh new file mode 100755 index 000000000..a09cb5ae7 --- /dev/null +++ b/ext/libpqxx-7.7.3/autogen.sh @@ -0,0 +1,44 @@ +#! /bin/sh +# Run this to generate the configure script etc. + +set -eu + +PQXXVERSION=$(./tools/extract_version) +PQXX_ABI=$(./tools/extract_version --abi) +PQXX_MAJOR=$(./tools/extract_version --major) +PQXX_MINOR=$(./tools/extract_version --minor) +echo "libpqxx version $PQXXVERSION" +echo "libpqxx ABI version $PQXX_ABI" + +substitute() { + sed -e "s/@PQXXVERSION@/$PQXXVERSION/g" \ + -e "s/@PQXX_MAJOR@/$PQXX_MAJOR/g" \ + -e "s/@PQXX_MINOR@/$PQXX_MINOR/g" \ + -e "s/@PQXX_ABI@/$PQXX_ABI/g" \ + "$1" +} + + +# Use templating system to generate various Makefiles. +expand_templates() { + for template in "$@" + do + ./tools/template2mak.py "$template" "${template%.template}" + done +} + + +# We have two kinds of templates. One uses our custom templating tool. And +# a few others simply have some substitutions done. +expand_templates $(find -name \*.template) +substitute include/pqxx/version.hxx.template >include/pqxx/version.hxx +substitute include/pqxx/doc/mainpage.md.template >include/pqxx/doc/mainpage.md + + +autoheader +libtoolize --force --automake --copy +aclocal -I . -I config/m4 +automake --add-missing --copy +autoconf + +echo "Done." diff --git a/ext/libpqxx-7.7.3/cmake/config.cmake b/ext/libpqxx-7.7.3/cmake/config.cmake new file mode 100644 index 000000000..816ee4474 --- /dev/null +++ b/ext/libpqxx-7.7.3/cmake/config.cmake @@ -0,0 +1,157 @@ +function(detect_code_compiled code macro msg) + message(STATUS "Detecting ${msg}") + check_cxx_source_compiles("${code}" "${macro}" FAIL_REGEX "warning") + if(${macro}) + message(STATUS "Detecting ${msg} - supported") + else() + message(STATUS "Detecting ${msg} - not supported") + endif() +endfunction(detect_code_compiled) + +include(CheckIncludeFileCXX) +include(CheckFunctionExists) +include(CheckSymbolExists) +include(CMakeDetermineCompileFeatures) +include(CheckCXXSourceCompiles) +include(CMakeFindDependencyMacro) + +if(NOT PostgreSQL_FOUND) + if(POLICY CMP0074) + cmake_policy(PUSH) + # CMP0074 is `OLD` by `cmake_minimum_required(VERSION 3.7)`, + # sets `NEW` to enable support CMake variable `PostgreSQL_ROOT`. + cmake_policy(SET CMP0074 NEW) + endif() + + find_package(PostgreSQL REQUIRED) + + if(POLICY CMP0074) + cmake_policy(POP) + endif() +endif() + +check_function_exists("poll" PQXX_HAVE_POLL) + +set(CMAKE_REQUIRED_LIBRARIES pq) +check_symbol_exists( + PQencryptPasswordConn + "${PostgreSQL_INCLUDE_DIR}/libpq-fe.h" + PQXX_HAVE_PQENCRYPTPASSWORDCONN) +check_symbol_exists( + PQenterPipelineMode + "${PostgreSQL_INCLUDE_DIR}/libpq-fe.h" + PQXX_HAVE_PQ_PIPELINE) + +cmake_determine_compile_features(CXX) +cmake_policy(SET CMP0057 NEW) + +# check_cxx_source_compiles requires CMAKE_REQUIRED_DEFINITIONS to specify +# compiling arguments. +# Wordaround: Push CMAKE_REQUIRED_DEFINITIONS +if(CMAKE_REQUIRED_DEFINITIONS) + set(def "${CMAKE_REQUIRED_DEFINITIONS}") +endif() +set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION}) +set(CMAKE_REQUIRED_QUIET ON) + +try_compile( + PQXX_HAVE_GCC_PURE + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/gcc_pure.cxx) +try_compile( + PQXX_HAVE_GCC_VISIBILITY + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/gcc_visibility.cxx) +try_compile( + PQXX_HAVE_LIKELY + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/likely.cxx) +try_compile( + PQXX_HAVE_CXA_DEMANGLE + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/cxa_demangle.cxx) +try_compile( + PQXX_HAVE_CONCEPTS + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/concepts.cxx) +try_compile( + PQXX_HAVE_SPAN + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/span.cxx) +try_compile( + PQXX_HAVE_MULTIDIMENSIONAL_SUBSCRIPT + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/multidim-subscript.cxx) +try_compile( + PQXX_HAVE_CHARCONV_FLOAT + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/charconv_float.cxx) +try_compile( + PQXX_HAVE_CHARCONV_INT + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/charconv_int.cxx) +try_compile( + PQXX_HAVE_PATH + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/fs.cxx) +try_compile( + PQXX_HAVE_THREAD_LOCAL + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/thread_local.cxx) +try_compile( + PQXX_HAVE_SLEEP_FOR + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/sleep_for.cxx) +try_compile( + PQXX_HAVE_STRERROR_R + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/strerror_r.cxx) +try_compile( + PQXX_HAVE_STRERROR_S + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/strerror_s.cxx) +try_compile( + PQXX_HAVE_YEAR_MONTH_DAY + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/year_month_day.cxx) +try_compile( + PQXX_HAVE_CMP + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/cmp.cxx) + +try_compile( + need_fslib + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/need_fslib.cxx) +if(!need_fslib) + # TODO: This may work for gcc 8, but some clang versions may need -lc++fs. + link_libraries(stdc++fs) +endif() + +# check_cxx_source_compiles requires CMAKE_REQUIRED_DEFINITIONS to specify +# compiling arguments. +# Workaround: Pop CMAKE_REQUIRED_DEFINITIONS +if(def) + set(CMAKE_REQUIRED_DEFINITIONS ${def}) + unset(def CACHE) +else() + unset(CMAKE_REQUIRED_DEFINITIONS CACHE) +endif() +set(CMAKE_REQUIRED_QUIET OFF) + +set(AC_CONFIG_H_IN "${PROJECT_SOURCE_DIR}/include/pqxx/config.h.in") +set(CM_CONFIG_H_IN "${PROJECT_BINARY_DIR}/include/pqxx/config_cmake.h.in") +set(CM_CONFIG_PUB "${PROJECT_BINARY_DIR}/include/pqxx/config-public-compiler.h") +set(CM_CONFIG_INT "${PROJECT_BINARY_DIR}/include/pqxx/config-internal-compiler.h") +set(CM_CONFIG_PQ "${PROJECT_BINARY_DIR}/include/pqxx/config-internal-libpq.h") +message(STATUS "Generating config.h") +file(WRITE "${CM_CONFIG_H_IN}" "") +file(STRINGS "${AC_CONFIG_H_IN}" lines) +foreach(line ${lines}) + string(REGEX REPLACE "^#undef" "#cmakedefine" l "${line}") + file(APPEND "${CM_CONFIG_H_IN}" "${l}\n") +endforeach() +configure_file("${CM_CONFIG_H_IN}" "${CM_CONFIG_INT}" @ONLY) +configure_file("${CM_CONFIG_H_IN}" "${CM_CONFIG_PUB}" @ONLY) +configure_file("${CM_CONFIG_H_IN}" "${CM_CONFIG_PQ}" @ONLY) +message(STATUS "Generating config.h - done") diff --git a/ext/libpqxx-7.7.3/cmake/libpqxx-config.cmake b/ext/libpqxx-7.7.3/cmake/libpqxx-config.cmake new file mode 100644 index 000000000..cb25a05f2 --- /dev/null +++ b/ext/libpqxx-7.7.3/cmake/libpqxx-config.cmake @@ -0,0 +1,4 @@ +include(CMakeFindDependencyMacro) +find_dependency(PostgreSQL) + +include("${CMAKE_CURRENT_LIST_DIR}/libpqxx-targets.cmake") diff --git a/ext/libpqxx-7.7.3/compile_flags.in b/ext/libpqxx-7.7.3/compile_flags.in new file mode 100644 index 000000000..1304e8aa8 --- /dev/null +++ b/ext/libpqxx-7.7.3/compile_flags.in @@ -0,0 +1 @@ +@CPPFLAGS@ @CXXFLAGS@ diff --git a/ext/libpqxx-7.7.3/config-tests/README.md b/ext/libpqxx-7.7.3/config-tests/README.md new file mode 100644 index 000000000..b3865d0f0 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/README.md @@ -0,0 +1,22 @@ +Configuration tests +=================== + +Libpqxx comes with support for different build systems: the GNU autotools, +CMake, Visual Studio's "nmake", and raw GNU "make" on Windows. + +For several of these build systems, we need to test things like "does this +compiler environment support `std::to_chars` for floating-point types?" + +We test these things by trying to compile a particular snippet of code, and +seeing whether that succeeds. + +To avoid duplicating those snippets for multiple build systems, we put them +here. Both the autotools configuration and the CMake configuration can refer to +them that way. + +It took a bit of nasty magic to read a C++ source file into m4 and treat it as +a string literal, without macro expansion. There is every chance that I missed +something, so be prepared for tests failing for unexpected reasons! Some C++ +syntax may end up having an unforeseen meaning in m4, and screw up the handling +of the code snippet. Re-configure, and read your logs carefully after editing +these snippets. diff --git a/ext/libpqxx-7.7.3/config-tests/charconv_float.cxx b/ext/libpqxx-7.7.3/config-tests/charconv_float.cxx new file mode 100644 index 000000000..bc5d973e3 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/charconv_float.cxx @@ -0,0 +1,16 @@ +// Test for std::to_string/std::from_string for floating-point types. +#include +#include + +int main() +{ + char z[100]; + auto rt = std::to_chars(std::begin(z), std::end(z), 3.14159L); + if (rt.ec != std::errc{}) + return 1; + long double n; + auto rf = std::from_chars(std::cbegin(z), std::cend(z), n); + if (rf.ec != std::errc{}) + return 2; + return (n > 3 and n < 4) ? 0 : 1; +} diff --git a/ext/libpqxx-7.7.3/config-tests/charconv_int.cxx b/ext/libpqxx-7.7.3/config-tests/charconv_int.cxx new file mode 100644 index 000000000..076ee0de3 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/charconv_int.cxx @@ -0,0 +1,16 @@ +// Test for std::to_string/std::from_string for integral types. +#include +#include + +int main() +{ + char z[100]; + auto rt = std::to_chars(std::begin(z), std::end(z), 9ULL); + if (rt.ec != std::errc{}) + return 1; + unsigned long long n; + auto rf = std::from_chars(std::cbegin(z), std::cend(z), n); + if (rf.ec != std::errc{}) + return 2; + return (n == 9ULL) ? 0 : 1; +} diff --git a/ext/libpqxx-7.7.3/config-tests/cmp.cxx b/ext/libpqxx-7.7.3/config-tests/cmp.cxx new file mode 100644 index 000000000..7f5abaa5d --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/cmp.cxx @@ -0,0 +1,8 @@ +// Test for C++20 std::cmp_greater etc. support. +#include + + +int main() +{ + return std::cmp_greater(-1, 2u) && std::cmp_less_equal(3, 0); +} diff --git a/ext/libpqxx-7.7.3/config-tests/concepts.cxx b/ext/libpqxx-7.7.3/config-tests/concepts.cxx new file mode 100644 index 000000000..5589b4e58 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/concepts.cxx @@ -0,0 +1,21 @@ +// Test for concepts support. Not just the language feature; also headers. +#include +#include +#include + + +template +concept Foo = std::ranges::input_range; + + +template auto foo(F const &r) +{ + return std::distance(std::begin(r), std::end(r)); +} + + +int main() +{ + std::vector const v{1, 2, 3}; + std::cout << foo(v) << '\n'; +} diff --git a/ext/libpqxx-7.7.3/config-tests/cxa_demangle.cxx b/ext/libpqxx-7.7.3/config-tests/cxa_demangle.cxx new file mode 100644 index 000000000..4d4ee7e6b --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/cxa_demangle.cxx @@ -0,0 +1,19 @@ +// Test for cross-vendor C++ ABI's __cxa_demangle function. +#include +#include +#include +#include + +#include + +int main() +{ + int status = 0; + char *name = + abi::__cxa_demangle(typeid(10).name(), nullptr, nullptr, &status); + if (status != 0) + throw std::runtime_error("Demangle failed!"); + int result = std::strcmp(name, "int"); + std::free(name); + return result; +} diff --git a/ext/libpqxx-7.7.3/config-tests/fs.cxx b/ext/libpqxx-7.7.3/config-tests/fs.cxx new file mode 100644 index 000000000..d93d37f5d --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/fs.cxx @@ -0,0 +1,9 @@ +// Check for working std::filesystem support. +#include + + +int main() +{ + // Apparently some versions of MinGW lack this comparison operator. + return std::filesystem::path{} != std::filesystem::path{}; +} diff --git a/ext/libpqxx-7.7.3/config-tests/gcc_pure.cxx b/ext/libpqxx-7.7.3/config-tests/gcc_pure.cxx new file mode 100644 index 000000000..4edd267c6 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/gcc_pure.cxx @@ -0,0 +1,10 @@ +// Test for gcc-style "pure" attribute. +int __attribute__((pure)) f() +{ + return 0; +} + +int main() +{ + return f(); +} diff --git a/ext/libpqxx-7.7.3/config-tests/gcc_visibility.cxx b/ext/libpqxx-7.7.3/config-tests/gcc_visibility.cxx new file mode 100644 index 000000000..23d0111d7 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/gcc_visibility.cxx @@ -0,0 +1,12 @@ +// Test for gcc-style "visibility" attribute. +struct __attribute__((visibility("hidden"))) D +{ + D() {} + int f() { return 0; } +}; + +int main() +{ + D d; + return d.f(); +} diff --git a/ext/libpqxx-7.7.3/config-tests/likely.cxx b/ext/libpqxx-7.7.3/config-tests/likely.cxx new file mode 100644 index 000000000..9d0eca8d3 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/likely.cxx @@ -0,0 +1,15 @@ +// Test for C++20 [[likely]] and [[unlikely]] attributes. + +int main(int argc, char **) +{ +#if __cplusplus < 202002L + deliberately_fail(because, older, C++, standard); +#endif + + int x = 0; + if (argc == 1) [[likely]] + x = 0; + else + x = 1; + return x; +} diff --git a/ext/libpqxx-7.7.3/config-tests/multidim-subscript.cxx b/ext/libpqxx-7.7.3/config-tests/multidim-subscript.cxx new file mode 100644 index 000000000..f75d1aa7a --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/multidim-subscript.cxx @@ -0,0 +1,14 @@ +// Test for multidimensional subscript operator support. +// Proposed for C++23: P2128R6. +struct table +{ + int width = 100; + + int operator[](int x, int y) const { return x + width * y; } +}; + + +int main() +{ + return table{}[0, 0]; +} diff --git a/ext/libpqxx-7.7.3/config-tests/need_fslib.cxx b/ext/libpqxx-7.7.3/config-tests/need_fslib.cxx new file mode 100644 index 000000000..041c46b43 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/need_fslib.cxx @@ -0,0 +1,21 @@ +// Check whether we need to link to the stdc++fs library. +// +// We assume that the presence of the header means that we have +// support for the basics of std::filesystem. This check will succeed if +// either there is no header, or there is one and it works without +// any special options. If the link fails, we assume that -lstdc++fs will fix +// it for us. + +#include + +#if __has_include() +# include +#endif + + +int main() +{ +#if __has_include() + std::cout << std::filesystem::path{"foo.bar"}.c_str() << '\n'; +#endif +} diff --git a/ext/libpqxx-7.7.3/config-tests/poll.cxx b/ext/libpqxx-7.7.3/config-tests/poll.cxx new file mode 100644 index 000000000..d67cef55d --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/poll.cxx @@ -0,0 +1,7 @@ +// Test for poll(). +#include + +int main() +{ + return poll(nullptr, 0, 0); +} diff --git a/ext/libpqxx-7.7.3/config-tests/sleep_for.cxx b/ext/libpqxx-7.7.3/config-tests/sleep_for.cxx new file mode 100644 index 000000000..f081fe0e5 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/sleep_for.cxx @@ -0,0 +1,28 @@ +// Test for std::this_thread::sleep_for(). +/* For some reason MinGW's header seems to be broken. + * + * But it gets worse. It looks as if we can include without problems + * in this configuration test. Why does it break when MinGW users try to build + * the library, but succeed when we try it here? + * + * To try and get close to the situation in the library code itself, we try + * including some standard headers that we don't strictly need here. + */ + +#if __has_include() +# include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +int main() +{ + std::this_thread::sleep_for(std::chrono::microseconds{10u}); +} diff --git a/ext/libpqxx-7.7.3/config-tests/span.cxx b/ext/libpqxx-7.7.3/config-tests/span.cxx new file mode 100644 index 000000000..8c13b97c9 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/span.cxx @@ -0,0 +1,8 @@ +// Test for std::span. +#include + +int main(int argc, char **argv) +{ + std::span args{argv, static_cast(argc)}; + return static_cast(std::size(args) - 1u); +} diff --git a/ext/libpqxx-7.7.3/config-tests/strerror_r.cxx b/ext/libpqxx-7.7.3/config-tests/strerror_r.cxx new file mode 100644 index 000000000..604d3b899 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/strerror_r.cxx @@ -0,0 +1,14 @@ +// Check for strerror_r. +// It can be either the POSIX version (which returns int) or the GNU version +// (which returns char *). + +#include +#include + +int main() +{ + char buffer[200]; + auto res{strerror_r(1, buffer, 200)}; + // Sidestep type differences. We don't really care what the value is. + return not not res; +} diff --git a/ext/libpqxx-7.7.3/config-tests/strerror_s.cxx b/ext/libpqxx-7.7.3/config-tests/strerror_s.cxx new file mode 100644 index 000000000..f6f99a7a5 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/strerror_s.cxx @@ -0,0 +1,11 @@ +// Test for strerror_s, as defined in Windows and C11. +// Presumably this'll be part of the C++ standard some day. + +#include + +int main() +{ + using namespace std; + char buf[200]; + return strerror_s(buf, 200, 1); +} diff --git a/ext/libpqxx-7.7.3/config-tests/thread_local.cxx b/ext/libpqxx-7.7.3/config-tests/thread_local.cxx new file mode 100644 index 000000000..b5a98b7b8 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/thread_local.cxx @@ -0,0 +1,15 @@ +// Test for std::to_string/std::from_string for floating-point types. +#include +#include + +int main(int argc, char **) +{ +#if defined(__MINGW32__) && defined(__GNUC__) +# if __GNUC__ < 11 || ((__GNUC__ == 11) && (__GNU_MINOR__ == 0)) +# error "On MinGW before gcc 11.1, thread_local breaks at run time." +# endif +#endif + thread_local std::stringstream s; + s << argc; + std::cout << s.str(); +} diff --git a/ext/libpqxx-7.7.3/config-tests/year_month_day.cxx b/ext/libpqxx-7.7.3/config-tests/year_month_day.cxx new file mode 100644 index 000000000..dbe543b55 --- /dev/null +++ b/ext/libpqxx-7.7.3/config-tests/year_month_day.cxx @@ -0,0 +1,7 @@ +// Test for std::chrono::year_month_day etc. +#include + +int main() +{ + return int(std::chrono::year{1}); +} diff --git a/ext/libpqxx-7.7.3/config/Makefile.am b/ext/libpqxx-7.7.3/config/Makefile.am new file mode 100644 index 000000000..f34b3107d --- /dev/null +++ b/ext/libpqxx-7.7.3/config/Makefile.am @@ -0,0 +1,8 @@ +EXTRA_DIST=m4/Makefile.am sample-headers +MAINTAINERCLEANFILES=Makefile.in config.guess config.sub install-sh \ + ltmain.sh missing mkinstalldirs + +dist-hook: + find "${distdir}" -type d -name CVS -print0 | xargs -0 rm -rf + find "${distdir}" -type d -name .svn -print0 | xargs -0 rm -rf + diff --git a/ext/libpqxx-7.7.3/config/Makefile.in b/ext/libpqxx-7.7.3/config/Makefile.in new file mode 100644 index 000000000..bf7aa004a --- /dev/null +++ b/ext/libpqxx-7.7.3/config/Makefile.in @@ -0,0 +1,470 @@ +# Makefile.in generated by automake 1.16.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = config +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/config/m4/libtool.m4 \ + $(top_srcdir)/config/m4/ltoptions.m4 \ + $(top_srcdir)/config/m4/ltsugar.m4 \ + $(top_srcdir)/config/m4/ltversion.m4 \ + $(top_srcdir)/config/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/include/pqxx/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in compile config.guess \ + config.sub install-sh ltmain.sh missing mkinstalldirs +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PG_CONFIG = @PG_CONFIG@ +PKG_CONFIG = @PKG_CONFIG@ +POSTGRES_INCLUDE = @POSTGRES_INCLUDE@ +PQXXVERSION = @PQXXVERSION@ +PQXX_ABI = @PQXX_ABI@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_postgres_lib = @with_postgres_lib@ +EXTRA_DIST = m4/Makefile.am sample-headers +MAINTAINERCLEANFILES = Makefile.in config.guess config.sub install-sh \ + ltmain.sh missing mkinstalldirs + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu config/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu config/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am dist-hook distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +dist-hook: + find "${distdir}" -type d -name CVS -print0 | xargs -0 rm -rf + find "${distdir}" -type d -name .svn -print0 | xargs -0 rm -rf + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ext/libpqxx-7.7.3/config/compile b/ext/libpqxx-7.7.3/config/compile new file mode 100755 index 000000000..df363c8fb --- /dev/null +++ b/ext/libpqxx-7.7.3/config/compile @@ -0,0 +1,348 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 1999-2021 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN* | MSYS*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/* | msys/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ + icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/ext/libpqxx-7.7.3/config/config.guess b/ext/libpqxx-7.7.3/config/config.guess new file mode 100755 index 000000000..c2246a4f7 --- /dev/null +++ b/ext/libpqxx-7.7.3/config/config.guess @@ -0,0 +1,1502 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +# Free Software Foundation, Inc. + +timestamp='2009-12-30' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner. Please send patches (context +# diff format) to and include a ChangeLog +# entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free +Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[456]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + case ${UNAME_MACHINE} in + pc98) + echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-gnu + else + echo ${UNAME_MACHINE}-unknown-linux-gnueabi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + LIBC=gnu + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + or32:Linux:*:*) + echo or32-unknown-linux-gnu + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-gnu + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + i386) + eval $set_cc_for_build + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + UNAME_PROCESSOR="x86_64" + fi + fi ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/ext/libpqxx-7.7.3/config/config.sub b/ext/libpqxx-7.7.3/config/config.sub new file mode 100755 index 000000000..c2d125724 --- /dev/null +++ b/ext/libpqxx-7.7.3/config/config.sub @@ -0,0 +1,1714 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +# Free Software Foundation, Inc. + +timestamp='2010-01-22' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Please send patches to . Submit a context +# diff and a properly formatted GNU ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free +Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ + uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nios | nios2 \ + | ns16k | ns32k \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e \ + | we32k \ + | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12 | picochip) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nios-* | nios2-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile-* | tilegx-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze) + basic_machine=microblaze-xilinx + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + # This must be matched before tile*. + tilegx*) + basic_machine=tilegx-unknown + os=-linux-gnu + ;; + tile*) + basic_machine=tile-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/ext/libpqxx-7.7.3/config/depcomp b/ext/libpqxx-7.7.3/config/depcomp new file mode 100755 index 000000000..715e34311 --- /dev/null +++ b/ext/libpqxx-7.7.3/config/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 1999-2021 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/ext/libpqxx-7.7.3/config/install-sh b/ext/libpqxx-7.7.3/config/install-sh new file mode 100755 index 000000000..6781b987b --- /dev/null +++ b/ext/libpqxx-7.7.3/config/install-sh @@ -0,0 +1,520 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2009-04-28.21; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + trap '(exit $?); exit' 1 2 13 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dst_arg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writeable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + -*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test -z "$d" && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/ext/libpqxx-7.7.3/config/ltmain.sh b/ext/libpqxx-7.7.3/config/ltmain.sh new file mode 100755 index 000000000..21e5e0784 --- /dev/null +++ b/ext/libpqxx-7.7.3/config/ltmain.sh @@ -0,0 +1,11251 @@ +#! /bin/sh +## DO NOT EDIT - This file generated from ./build-aux/ltmain.in +## by inline-source v2014-01-03.01 + +# libtool (GNU libtool) 2.4.6 +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit , 1996 + +# Copyright (C) 1996-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +PROGRAM=libtool +PACKAGE=libtool +VERSION="2.4.6 Debian-2.4.6-15" +package_revision=2.4.6 + + +## ------ ## +## Usage. ## +## ------ ## + +# Run './libtool --help' for help with using this script from the +# command line. + + +## ------------------------------- ## +## User overridable command paths. ## +## ------------------------------- ## + +# After configure completes, it has a better idea of some of the +# shell tools we need than the defaults used by the functions shared +# with bootstrap, so set those here where they can still be over- +# ridden by the user, but otherwise take precedence. + +: ${AUTOCONF="autoconf"} +: ${AUTOMAKE="automake"} + + +## -------------------------- ## +## Source external libraries. ## +## -------------------------- ## + +# Much of our low-level functionality needs to be sourced from external +# libraries, which are installed to $pkgauxdir. + +# Set a version string for this script. +scriptversion=2015-01-20.17; # UTC + +# General shell script boiler plate, and helper functions. +# Written by Gary V. Vaughan, 2004 + +# Copyright (C) 2004-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +# As a special exception to the GNU General Public License, if you distribute +# this file as part of a program or library that is built using GNU Libtool, +# you may include this file under the same distribution terms that you use +# for the rest of that program. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Please report bugs or propose patches to gary@gnu.org. + + +## ------ ## +## Usage. ## +## ------ ## + +# Evaluate this file near the top of your script to gain access to +# the functions and variables defined here: +# +# . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh +# +# If you need to override any of the default environment variable +# settings, do that before evaluating this file. + + +## -------------------- ## +## Shell normalisation. ## +## -------------------- ## + +# Some shells need a little help to be as Bourne compatible as possible. +# Before doing anything else, make sure all that help has been provided! + +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac +fi + +# NLS nuisances: We save the old values in case they are required later. +_G_user_locale= +_G_safe_locale= +for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test set = \"\${$_G_var+set}\"; then + save_$_G_var=\$$_G_var + $_G_var=C + export $_G_var + _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" + _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" + fi" +done + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Make sure IFS has a sensible default +sp=' ' +nl=' +' +IFS="$sp $nl" + +# There are apparently some retarded systems that use ';' as a PATH separator! +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + + +## ------------------------- ## +## Locate command utilities. ## +## ------------------------- ## + + +# func_executable_p FILE +# ---------------------- +# Check that FILE is an executable regular file. +func_executable_p () +{ + test -f "$1" && test -x "$1" +} + + +# func_path_progs PROGS_LIST CHECK_FUNC [PATH] +# -------------------------------------------- +# Search for either a program that responds to --version with output +# containing "GNU", or else returned by CHECK_FUNC otherwise, by +# trying all the directories in PATH with each of the elements of +# PROGS_LIST. +# +# CHECK_FUNC should accept the path to a candidate program, and +# set $func_check_prog_result if it truncates its output less than +# $_G_path_prog_max characters. +func_path_progs () +{ + _G_progs_list=$1 + _G_check_func=$2 + _G_PATH=${3-"$PATH"} + + _G_path_prog_max=0 + _G_path_prog_found=false + _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} + for _G_dir in $_G_PATH; do + IFS=$_G_save_IFS + test -z "$_G_dir" && _G_dir=. + for _G_prog_name in $_G_progs_list; do + for _exeext in '' .EXE; do + _G_path_prog=$_G_dir/$_G_prog_name$_exeext + func_executable_p "$_G_path_prog" || continue + case `"$_G_path_prog" --version 2>&1` in + *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; + *) $_G_check_func $_G_path_prog + func_path_progs_result=$func_check_prog_result + ;; + esac + $_G_path_prog_found && break 3 + done + done + done + IFS=$_G_save_IFS + test -z "$func_path_progs_result" && { + echo "no acceptable sed could be found in \$PATH" >&2 + exit 1 + } +} + + +# We want to be able to use the functions in this file before configure +# has figured out where the best binaries are kept, which means we have +# to search for them ourselves - except when the results are already set +# where we skip the searches. + +# Unless the user overrides by setting SED, search the path for either GNU +# sed, or the sed that truncates its output the least. +test -z "$SED" && { + _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for _G_i in 1 2 3 4 5 6 7; do + _G_sed_script=$_G_sed_script$nl$_G_sed_script + done + echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed + _G_sed_script= + + func_check_prog_sed () + { + _G_path_prog=$1 + + _G_count=0 + printf 0123456789 >conftest.in + while : + do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo '' >> conftest.nl + "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break + diff conftest.out conftest.nl >/dev/null 2>&1 || break + _G_count=`expr $_G_count + 1` + if test "$_G_count" -gt "$_G_path_prog_max"; then + # Best one so far, save it but keep looking for a better one + func_check_prog_result=$_G_path_prog + _G_path_prog_max=$_G_count + fi + # 10*(2^10) chars as input seems more than enough + test 10 -lt "$_G_count" && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out + } + + func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin + rm -f conftest.sed + SED=$func_path_progs_result +} + + +# Unless the user overrides by setting GREP, search the path for either GNU +# grep, or the grep that truncates its output the least. +test -z "$GREP" && { + func_check_prog_grep () + { + _G_path_prog=$1 + + _G_count=0 + _G_path_prog_max=0 + printf 0123456789 >conftest.in + while : + do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo 'GREP' >> conftest.nl + "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break + diff conftest.out conftest.nl >/dev/null 2>&1 || break + _G_count=`expr $_G_count + 1` + if test "$_G_count" -gt "$_G_path_prog_max"; then + # Best one so far, save it but keep looking for a better one + func_check_prog_result=$_G_path_prog + _G_path_prog_max=$_G_count + fi + # 10*(2^10) chars as input seems more than enough + test 10 -lt "$_G_count" && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out + } + + func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin + GREP=$func_path_progs_result +} + + +## ------------------------------- ## +## User overridable command paths. ## +## ------------------------------- ## + +# All uppercase variable names are used for environment variables. These +# variables can be overridden by the user before calling a script that +# uses them if a suitable command of that name is not already available +# in the command search PATH. + +: ${CP="cp -f"} +: ${ECHO="printf %s\n"} +: ${EGREP="$GREP -E"} +: ${FGREP="$GREP -F"} +: ${LN_S="ln -s"} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} + + +## -------------------- ## +## Useful sed snippets. ## +## -------------------- ## + +sed_dirname='s|/[^/]*$||' +sed_basename='s|^.*/||' + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Same as above, but do not quote variable references. +sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution that turns a string into a regex matching for the +# string literally. +sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' + +# Sed substitution that converts a w32 file name or path +# that contains forward slashes, into one that contains +# (escaped) backslashes. A very naive implementation. +sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + +# Re-'\' parameter expansions in output of sed_double_quote_subst that +# were '\'-ed in input to the same. If an odd number of '\' preceded a +# '$' in input to sed_double_quote_subst, that '$' was protected from +# expansion. Since each input '\' is now two '\'s, look for any number +# of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. +_G_bs='\\' +_G_bs2='\\\\' +_G_bs4='\\\\\\\\' +_G_dollar='\$' +sed_double_backslash="\ + s/$_G_bs4/&\\ +/g + s/^$_G_bs2$_G_dollar/$_G_bs&/ + s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g + s/\n//g" + + +## ----------------- ## +## Global variables. ## +## ----------------- ## + +# Except for the global variables explicitly listed below, the following +# functions in the '^func_' namespace, and the '^require_' namespace +# variables initialised in the 'Resource management' section, sourcing +# this file will not pollute your global namespace with anything +# else. There's no portable way to scope variables in Bourne shell +# though, so actually running these functions will sometimes place +# results into a variable named after the function, and often use +# temporary variables in the '^_G_' namespace. If you are careful to +# avoid using those namespaces casually in your sourcing script, things +# should continue to work as you expect. And, of course, you can freely +# overwrite any of the functions or variables defined here before +# calling anything to customize them. + +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +# Allow overriding, eg assuming that you follow the convention of +# putting '$debug_cmd' at the start of all your functions, you can get +# bash to show function call trace with: +# +# debug_cmd='echo "${FUNCNAME[0]} $*" >&2' bash your-script-name +debug_cmd=${debug_cmd-":"} +exit_cmd=: + +# By convention, finish your script with: +# +# exit $exit_status +# +# so that you can set exit_status to non-zero if you want to indicate +# something went wrong during execution without actually bailing out at +# the point of failure. +exit_status=$EXIT_SUCCESS + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath=$0 + +# The name of this program. +progname=`$ECHO "$progpath" |$SED "$sed_basename"` + +# Make sure we have an absolute progpath for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` + progdir=`cd "$progdir" && pwd` + progpath=$progdir/$progname + ;; + *) + _G_IFS=$IFS + IFS=${PATH_SEPARATOR-:} + for progdir in $PATH; do + IFS=$_G_IFS + test -x "$progdir/$progname" && break + done + IFS=$_G_IFS + test -n "$progdir" || progdir=`pwd` + progpath=$progdir/$progname + ;; +esac + + +## ----------------- ## +## Standard options. ## +## ----------------- ## + +# The following options affect the operation of the functions defined +# below, and should be set appropriately depending on run-time para- +# meters passed on the command line. + +opt_dry_run=false +opt_quiet=false +opt_verbose=false + +# Categories 'all' and 'none' are always available. Append any others +# you will pass as the first argument to func_warning from your own +# code. +warning_categories= + +# By default, display warnings according to 'opt_warning_types'. Set +# 'warning_func' to ':' to elide all warnings, or func_fatal_error to +# treat the next displayed warning as a fatal error. +warning_func=func_warn_and_continue + +# Set to 'all' to display all warnings, 'none' to suppress all +# warnings, or a space delimited list of some subset of +# 'warning_categories' to display only the listed warnings. +opt_warning_types=all + + +## -------------------- ## +## Resource management. ## +## -------------------- ## + +# This section contains definitions for functions that each ensure a +# particular resource (a file, or a non-empty configuration variable for +# example) is available, and if appropriate to extract default values +# from pertinent package files. Call them using their associated +# 'require_*' variable to ensure that they are executed, at most, once. +# +# It's entirely deliberate that calling these functions can set +# variables that don't obey the namespace limitations obeyed by the rest +# of this file, in order that that they be as useful as possible to +# callers. + + +# require_term_colors +# ------------------- +# Allow display of bold text on terminals that support it. +require_term_colors=func_require_term_colors +func_require_term_colors () +{ + $debug_cmd + + test -t 1 && { + # COLORTERM and USE_ANSI_COLORS environment variables take + # precedence, because most terminfo databases neglect to describe + # whether color sequences are supported. + test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} + + if test 1 = "$USE_ANSI_COLORS"; then + # Standard ANSI escape sequences + tc_reset='' + tc_bold=''; tc_standout='' + tc_red=''; tc_green='' + tc_blue=''; tc_cyan='' + else + # Otherwise trust the terminfo database after all. + test -n "`tput sgr0 2>/dev/null`" && { + tc_reset=`tput sgr0` + test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` + tc_standout=$tc_bold + test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` + test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` + test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` + test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` + test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` + } + fi + } + + require_term_colors=: +} + + +## ----------------- ## +## Function library. ## +## ----------------- ## + +# This section contains a variety of useful functions to call in your +# scripts. Take note of the portable wrappers for features provided by +# some modern shells, which will fall back to slower equivalents on +# less featureful shells. + + +# func_append VAR VALUE +# --------------------- +# Append VALUE onto the existing contents of VAR. + + # We should try to minimise forks, especially on Windows where they are + # unreasonably slow, so skip the feature probes when bash or zsh are + # being used: + if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then + : ${_G_HAVE_ARITH_OP="yes"} + : ${_G_HAVE_XSI_OPS="yes"} + # The += operator was introduced in bash 3.1 + case $BASH_VERSION in + [12].* | 3.0 | 3.0*) ;; + *) + : ${_G_HAVE_PLUSEQ_OP="yes"} + ;; + esac + fi + + # _G_HAVE_PLUSEQ_OP + # Can be empty, in which case the shell is probed, "yes" if += is + # useable or anything else if it does not work. + test -z "$_G_HAVE_PLUSEQ_OP" \ + && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ + && _G_HAVE_PLUSEQ_OP=yes + +if test yes = "$_G_HAVE_PLUSEQ_OP" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_append () + { + $debug_cmd + + eval "$1+=\$2" + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_append () + { + $debug_cmd + + eval "$1=\$$1\$2" + } +fi + + +# func_append_quoted VAR VALUE +# ---------------------------- +# Quote VALUE and append to the end of shell variable VAR, separated +# by a space. +if test yes = "$_G_HAVE_PLUSEQ_OP"; then + eval 'func_append_quoted () + { + $debug_cmd + + func_quote_for_eval "$2" + eval "$1+=\\ \$func_quote_for_eval_result" + }' +else + func_append_quoted () + { + $debug_cmd + + func_quote_for_eval "$2" + eval "$1=\$$1\\ \$func_quote_for_eval_result" + } +fi + + +# func_append_uniq VAR VALUE +# -------------------------- +# Append unique VALUE onto the existing contents of VAR, assuming +# entries are delimited by the first character of VALUE. For example: +# +# func_append_uniq options " --another-option option-argument" +# +# will only append to $options if " --another-option option-argument " +# is not already present somewhere in $options already (note spaces at +# each end implied by leading space in second argument). +func_append_uniq () +{ + $debug_cmd + + eval _G_current_value='`$ECHO $'$1'`' + _G_delim=`expr "$2" : '\(.\)'` + + case $_G_delim$_G_current_value$_G_delim in + *"$2$_G_delim"*) ;; + *) func_append "$@" ;; + esac +} + + +# func_arith TERM... +# ------------------ +# Set func_arith_result to the result of evaluating TERMs. + test -z "$_G_HAVE_ARITH_OP" \ + && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ + && _G_HAVE_ARITH_OP=yes + +if test yes = "$_G_HAVE_ARITH_OP"; then + eval 'func_arith () + { + $debug_cmd + + func_arith_result=$(( $* )) + }' +else + func_arith () + { + $debug_cmd + + func_arith_result=`expr "$@"` + } +fi + + +# func_basename FILE +# ------------------ +# Set func_basename_result to FILE with everything up to and including +# the last / stripped. +if test yes = "$_G_HAVE_XSI_OPS"; then + # If this shell supports suffix pattern removal, then use it to avoid + # forking. Hide the definitions single quotes in case the shell chokes + # on unsupported syntax... + _b='func_basename_result=${1##*/}' + _d='case $1 in + */*) func_dirname_result=${1%/*}$2 ;; + * ) func_dirname_result=$3 ;; + esac' + +else + # ...otherwise fall back to using sed. + _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' + _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` + if test "X$func_dirname_result" = "X$1"; then + func_dirname_result=$3 + else + func_append func_dirname_result "$2" + fi' +fi + +eval 'func_basename () +{ + $debug_cmd + + '"$_b"' +}' + + +# func_dirname FILE APPEND NONDIR_REPLACEMENT +# ------------------------------------------- +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +eval 'func_dirname () +{ + $debug_cmd + + '"$_d"' +}' + + +# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT +# -------------------------------------------------------- +# Perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# For efficiency, we do not delegate to the functions above but instead +# duplicate the functionality here. +eval 'func_dirname_and_basename () +{ + $debug_cmd + + '"$_b"' + '"$_d"' +}' + + +# func_echo ARG... +# ---------------- +# Echo program name prefixed message. +func_echo () +{ + $debug_cmd + + _G_message=$* + + func_echo_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_IFS + $ECHO "$progname: $_G_line" + done + IFS=$func_echo_IFS +} + + +# func_echo_all ARG... +# -------------------- +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + + +# func_echo_infix_1 INFIX ARG... +# ------------------------------ +# Echo program name, followed by INFIX on the first line, with any +# additional lines not showing INFIX. +func_echo_infix_1 () +{ + $debug_cmd + + $require_term_colors + + _G_infix=$1; shift + _G_indent=$_G_infix + _G_prefix="$progname: $_G_infix: " + _G_message=$* + + # Strip color escape sequences before counting printable length + for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" + do + test -n "$_G_tc" && { + _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` + _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` + } + done + _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes + + func_echo_infix_1_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_infix_1_IFS + $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 + _G_prefix=$_G_indent + done + IFS=$func_echo_infix_1_IFS +} + + +# func_error ARG... +# ----------------- +# Echo program name prefixed message to standard error. +func_error () +{ + $debug_cmd + + $require_term_colors + + func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 +} + + +# func_fatal_error ARG... +# ----------------------- +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + $debug_cmd + + func_error "$*" + exit $EXIT_FAILURE +} + + +# func_grep EXPRESSION FILENAME +# ----------------------------- +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $debug_cmd + + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_len STRING +# --------------- +# Set func_len_result to the length of STRING. STRING may not +# start with a hyphen. + test -z "$_G_HAVE_XSI_OPS" \ + && (eval 'x=a/b/c; + test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ + && _G_HAVE_XSI_OPS=yes + +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_len () + { + $debug_cmd + + func_len_result=${#1} + }' +else + func_len () + { + $debug_cmd + + func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` + } +fi + + +# func_mkdir_p DIRECTORY-PATH +# --------------------------- +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + $debug_cmd + + _G_directory_path=$1 + _G_dir_list= + + if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then + + # Protect directory names starting with '-' + case $_G_directory_path in + -*) _G_directory_path=./$_G_directory_path ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$_G_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + _G_dir_list=$_G_directory_path:$_G_dir_list + + # If the last portion added has no slash in it, the list is done + case $_G_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` + done + _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` + + func_mkdir_p_IFS=$IFS; IFS=: + for _G_dir in $_G_dir_list; do + IFS=$func_mkdir_p_IFS + # mkdir can fail with a 'File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$_G_dir" 2>/dev/null || : + done + IFS=$func_mkdir_p_IFS + + # Bail out if we (or some other process) failed to create a directory. + test -d "$_G_directory_path" || \ + func_fatal_error "Failed to create '$1'" + fi +} + + +# func_mktempdir [BASENAME] +# ------------------------- +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, BASENAME is the basename for that directory. +func_mktempdir () +{ + $debug_cmd + + _G_template=${TMPDIR-/tmp}/${1-$progname} + + if test : = "$opt_dry_run"; then + # Return a directory name, but don't create it in dry-run mode + _G_tmpdir=$_G_template-$$ + else + + # If mktemp works, use that first and foremost + _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` + + if test ! -d "$_G_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + _G_tmpdir=$_G_template-${RANDOM-0}$$ + + func_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$_G_tmpdir" + umask $func_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$_G_tmpdir" || \ + func_fatal_error "cannot create temporary directory '$_G_tmpdir'" + fi + + $ECHO "$_G_tmpdir" +} + + +# func_normal_abspath PATH +# ------------------------ +# Remove doubled-up and trailing slashes, "." path components, +# and cancel out any ".." path components in PATH after making +# it an absolute path. +func_normal_abspath () +{ + $debug_cmd + + # These SED scripts presuppose an absolute path with a trailing slash. + _G_pathcar='s|^/\([^/]*\).*$|\1|' + _G_pathcdr='s|^/[^/]*||' + _G_removedotparts=':dotsl + s|/\./|/|g + t dotsl + s|/\.$|/|' + _G_collapseslashes='s|/\{1,\}|/|g' + _G_finalslash='s|/*$|/|' + + # Start from root dir and reassemble the path. + func_normal_abspath_result= + func_normal_abspath_tpath=$1 + func_normal_abspath_altnamespace= + case $func_normal_abspath_tpath in + "") + # Empty path, that just means $cwd. + func_stripname '' '/' "`pwd`" + func_normal_abspath_result=$func_stripname_result + return + ;; + # The next three entries are used to spot a run of precisely + # two leading slashes without using negated character classes; + # we take advantage of case's first-match behaviour. + ///*) + # Unusual form of absolute path, do nothing. + ;; + //*) + # Not necessarily an ordinary path; POSIX reserves leading '//' + # and for example Cygwin uses it to access remote file shares + # over CIFS/SMB, so we conserve a leading double slash if found. + func_normal_abspath_altnamespace=/ + ;; + /*) + # Absolute path, do nothing. + ;; + *) + # Relative path, prepend $cwd. + func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath + ;; + esac + + # Cancel out all the simple stuff to save iterations. We also want + # the path to end with a slash for ease of parsing, so make sure + # there is one (and only one) here. + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` + while :; do + # Processed it all yet? + if test / = "$func_normal_abspath_tpath"; then + # If we ascended to the root using ".." the result may be empty now. + if test -z "$func_normal_abspath_result"; then + func_normal_abspath_result=/ + fi + break + fi + func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_pathcar"` + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_pathcdr"` + # Figure out what to do with it + case $func_normal_abspath_tcomponent in + "") + # Trailing empty path component, ignore it. + ;; + ..) + # Parent dir; strip last assembled component from result. + func_dirname "$func_normal_abspath_result" + func_normal_abspath_result=$func_dirname_result + ;; + *) + # Actual path component, append it. + func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" + ;; + esac + done + # Restore leading double-slash if one was found on entry. + func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result +} + + +# func_notquiet ARG... +# -------------------- +# Echo program name prefixed message only when not in quiet mode. +func_notquiet () +{ + $debug_cmd + + $opt_quiet || func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + + +# func_relative_path SRCDIR DSTDIR +# -------------------------------- +# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. +func_relative_path () +{ + $debug_cmd + + func_relative_path_result= + func_normal_abspath "$1" + func_relative_path_tlibdir=$func_normal_abspath_result + func_normal_abspath "$2" + func_relative_path_tbindir=$func_normal_abspath_result + + # Ascend the tree starting from libdir + while :; do + # check if we have found a prefix of bindir + case $func_relative_path_tbindir in + $func_relative_path_tlibdir) + # found an exact match + func_relative_path_tcancelled= + break + ;; + $func_relative_path_tlibdir*) + # found a matching prefix + func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" + func_relative_path_tcancelled=$func_stripname_result + if test -z "$func_relative_path_result"; then + func_relative_path_result=. + fi + break + ;; + *) + func_dirname $func_relative_path_tlibdir + func_relative_path_tlibdir=$func_dirname_result + if test -z "$func_relative_path_tlibdir"; then + # Have to descend all the way to the root! + func_relative_path_result=../$func_relative_path_result + func_relative_path_tcancelled=$func_relative_path_tbindir + break + fi + func_relative_path_result=../$func_relative_path_result + ;; + esac + done + + # Now calculate path; take care to avoid doubling-up slashes. + func_stripname '' '/' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + func_stripname '/' '/' "$func_relative_path_tcancelled" + if test -n "$func_stripname_result"; then + func_append func_relative_path_result "/$func_stripname_result" + fi + + # Normalisation. If bindir is libdir, return '.' else relative path. + if test -n "$func_relative_path_result"; then + func_stripname './' '' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + fi + + test -n "$func_relative_path_result" || func_relative_path_result=. + + : +} + + +# func_quote_for_eval ARG... +# -------------------------- +# Aesthetically quote ARGs to be evaled later. +# This function returns two values: +# i) func_quote_for_eval_result +# double-quoted, suitable for a subsequent eval +# ii) func_quote_for_eval_unquoted_result +# has all characters that are still active within double +# quotes backslashified. +func_quote_for_eval () +{ + $debug_cmd + + func_quote_for_eval_unquoted_result= + func_quote_for_eval_result= + while test 0 -lt $#; do + case $1 in + *[\\\`\"\$]*) + _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; + *) + _G_unquoted_arg=$1 ;; + esac + if test -n "$func_quote_for_eval_unquoted_result"; then + func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" + else + func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" + fi + + case $_G_unquoted_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and variable expansion + # for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + _G_quoted_arg=\"$_G_unquoted_arg\" + ;; + *) + _G_quoted_arg=$_G_unquoted_arg + ;; + esac + + if test -n "$func_quote_for_eval_result"; then + func_append func_quote_for_eval_result " $_G_quoted_arg" + else + func_append func_quote_for_eval_result "$_G_quoted_arg" + fi + shift + done +} + + +# func_quote_for_expand ARG +# ------------------------- +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + $debug_cmd + + case $1 in + *[\\\`\"]*) + _G_arg=`$ECHO "$1" | $SED \ + -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; + *) + _G_arg=$1 ;; + esac + + case $_G_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + _G_arg=\"$_G_arg\" + ;; + esac + + func_quote_for_expand_result=$_G_arg +} + + +# func_stripname PREFIX SUFFIX NAME +# --------------------------------- +# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_stripname () + { + $debug_cmd + + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary variable first. + func_stripname_result=$3 + func_stripname_result=${func_stripname_result#"$1"} + func_stripname_result=${func_stripname_result%"$2"} + }' +else + func_stripname () + { + $debug_cmd + + case $2 in + .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; + *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; + esac + } +fi + + +# func_show_eval CMD [FAIL_EXP] +# ----------------------------- +# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + $debug_cmd + + _G_cmd=$1 + _G_fail_exp=${2-':'} + + func_quote_for_expand "$_G_cmd" + eval "func_notquiet $func_quote_for_expand_result" + + $opt_dry_run || { + eval "$_G_cmd" + _G_status=$? + if test 0 -ne "$_G_status"; then + eval "(exit $_G_status); $_G_fail_exp" + fi + } +} + + +# func_show_eval_locale CMD [FAIL_EXP] +# ------------------------------------ +# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + $debug_cmd + + _G_cmd=$1 + _G_fail_exp=${2-':'} + + $opt_quiet || { + func_quote_for_expand "$_G_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + $opt_dry_run || { + eval "$_G_user_locale + $_G_cmd" + _G_status=$? + eval "$_G_safe_locale" + if test 0 -ne "$_G_status"; then + eval "(exit $_G_status); $_G_fail_exp" + fi + } +} + + +# func_tr_sh +# ---------- +# Turn $1 into a string suitable for a shell variable name. +# Result is stored in $func_tr_sh_result. All characters +# not in the set a-zA-Z0-9_ are replaced with '_'. Further, +# if $1 begins with a digit, a '_' is prepended as well. +func_tr_sh () +{ + $debug_cmd + + case $1 in + [0-9]* | *[!a-zA-Z0-9_]*) + func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` + ;; + * ) + func_tr_sh_result=$1 + ;; + esac +} + + +# func_verbose ARG... +# ------------------- +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $debug_cmd + + $opt_verbose && func_echo "$*" + + : +} + + +# func_warn_and_continue ARG... +# ----------------------------- +# Echo program name prefixed warning message to standard error. +func_warn_and_continue () +{ + $debug_cmd + + $require_term_colors + + func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 +} + + +# func_warning CATEGORY ARG... +# ---------------------------- +# Echo program name prefixed warning message to standard error. Warning +# messages can be filtered according to CATEGORY, where this function +# elides messages where CATEGORY is not listed in the global variable +# 'opt_warning_types'. +func_warning () +{ + $debug_cmd + + # CATEGORY must be in the warning_categories list! + case " $warning_categories " in + *" $1 "*) ;; + *) func_internal_error "invalid warning category '$1'" ;; + esac + + _G_category=$1 + shift + + case " $opt_warning_types " in + *" $_G_category "*) $warning_func ${1+"$@"} ;; + esac +} + + +# func_sort_ver VER1 VER2 +# ----------------------- +# 'sort -V' is not generally available. +# Note this deviates from the version comparison in automake +# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a +# but this should suffice as we won't be specifying old +# version formats or redundant trailing .0 in bootstrap.conf. +# If we did want full compatibility then we should probably +# use m4_version_compare from autoconf. +func_sort_ver () +{ + $debug_cmd + + printf '%s\n%s\n' "$1" "$2" \ + | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n +} + +# func_lt_ver PREV CURR +# --------------------- +# Return true if PREV and CURR are in the correct order according to +# func_sort_ver, otherwise false. Use it like this: +# +# func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." +func_lt_ver () +{ + $debug_cmd + + test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` +} + + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: +#! /bin/sh + +# Set a version string for this script. +scriptversion=2015-10-07.11; # UTC + +# A portable, pluggable option parser for Bourne shell. +# Written by Gary V. Vaughan, 2010 + +# Copyright (C) 2010-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Please report bugs or propose patches to gary@gnu.org. + + +## ------ ## +## Usage. ## +## ------ ## + +# This file is a library for parsing options in your shell scripts along +# with assorted other useful supporting features that you can make use +# of too. +# +# For the simplest scripts you might need only: +# +# #!/bin/sh +# . relative/path/to/funclib.sh +# . relative/path/to/options-parser +# scriptversion=1.0 +# func_options ${1+"$@"} +# eval set dummy "$func_options_result"; shift +# ...rest of your script... +# +# In order for the '--version' option to work, you will need to have a +# suitably formatted comment like the one at the top of this file +# starting with '# Written by ' and ending with '# warranty; '. +# +# For '-h' and '--help' to work, you will also need a one line +# description of your script's purpose in a comment directly above the +# '# Written by ' line, like the one at the top of this file. +# +# The default options also support '--debug', which will turn on shell +# execution tracing (see the comment above debug_cmd below for another +# use), and '--verbose' and the func_verbose function to allow your script +# to display verbose messages only when your user has specified +# '--verbose'. +# +# After sourcing this file, you can plug processing for additional +# options by amending the variables from the 'Configuration' section +# below, and following the instructions in the 'Option parsing' +# section further down. + +## -------------- ## +## Configuration. ## +## -------------- ## + +# You should override these variables in your script after sourcing this +# file so that they reflect the customisations you have added to the +# option parser. + +# The usage line for option parsing errors and the start of '-h' and +# '--help' output messages. You can embed shell variables for delayed +# expansion at the time the message is displayed, but you will need to +# quote other shell meta-characters carefully to prevent them being +# expanded when the contents are evaled. +usage='$progpath [OPTION]...' + +# Short help message in response to '-h' and '--help'. Add to this or +# override it after sourcing this library to reflect the full set of +# options your script accepts. +usage_message="\ + --debug enable verbose shell tracing + -W, --warnings=CATEGORY + report the warnings falling in CATEGORY [all] + -v, --verbose verbosely report processing + --version print version information and exit + -h, --help print short or long help message and exit +" + +# Additional text appended to 'usage_message' in response to '--help'. +long_help_message=" +Warning categories include: + 'all' show all warnings + 'none' turn off all the warnings + 'error' warnings are treated as fatal errors" + +# Help message printed before fatal option parsing errors. +fatal_help="Try '\$progname --help' for more information." + + + +## ------------------------- ## +## Hook function management. ## +## ------------------------- ## + +# This section contains functions for adding, removing, and running hooks +# to the main code. A hook is just a named list of of function, that can +# be run in order later on. + +# func_hookable FUNC_NAME +# ----------------------- +# Declare that FUNC_NAME will run hooks added with +# 'func_add_hook FUNC_NAME ...'. +func_hookable () +{ + $debug_cmd + + func_append hookable_fns " $1" +} + + +# func_add_hook FUNC_NAME HOOK_FUNC +# --------------------------------- +# Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must +# first have been declared "hookable" by a call to 'func_hookable'. +func_add_hook () +{ + $debug_cmd + + case " $hookable_fns " in + *" $1 "*) ;; + *) func_fatal_error "'$1' does not accept hook functions." ;; + esac + + eval func_append ${1}_hooks '" $2"' +} + + +# func_remove_hook FUNC_NAME HOOK_FUNC +# ------------------------------------ +# Remove HOOK_FUNC from the list of functions called by FUNC_NAME. +func_remove_hook () +{ + $debug_cmd + + eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' +} + + +# func_run_hooks FUNC_NAME [ARG]... +# --------------------------------- +# Run all hook functions registered to FUNC_NAME. +# It is assumed that the list of hook functions contains nothing more +# than a whitespace-delimited list of legal shell function names, and +# no effort is wasted trying to catch shell meta-characters or preserve +# whitespace. +func_run_hooks () +{ + $debug_cmd + + _G_rc_run_hooks=false + + case " $hookable_fns " in + *" $1 "*) ;; + *) func_fatal_error "'$1' does not support hook funcions.n" ;; + esac + + eval _G_hook_fns=\$$1_hooks; shift + + for _G_hook in $_G_hook_fns; do + if eval $_G_hook '"$@"'; then + # store returned options list back into positional + # parameters for next 'cmd' execution. + eval _G_hook_result=\$${_G_hook}_result + eval set dummy "$_G_hook_result"; shift + _G_rc_run_hooks=: + fi + done + + $_G_rc_run_hooks && func_run_hooks_result=$_G_hook_result +} + + + +## --------------- ## +## Option parsing. ## +## --------------- ## + +# In order to add your own option parsing hooks, you must accept the +# full positional parameter list in your hook function, you may remove/edit +# any options that you action, and then pass back the remaining unprocessed +# options in '_result', escaped suitably for +# 'eval'. In this case you also must return $EXIT_SUCCESS to let the +# hook's caller know that it should pay attention to +# '_result'. Returning $EXIT_FAILURE signalizes that +# arguments are left untouched by the hook and therefore caller will ignore the +# result variable. +# +# Like this: +# +# my_options_prep () +# { +# $debug_cmd +# +# # Extend the existing usage message. +# usage_message=$usage_message' +# -s, --silent don'\''t print informational messages +# ' +# # No change in '$@' (ignored completely by this hook). There is +# # no need to do the equivalent (but slower) action: +# # func_quote_for_eval ${1+"$@"} +# # my_options_prep_result=$func_quote_for_eval_result +# false +# } +# func_add_hook func_options_prep my_options_prep +# +# +# my_silent_option () +# { +# $debug_cmd +# +# args_changed=false +# +# # Note that for efficiency, we parse as many options as we can +# # recognise in a loop before passing the remainder back to the +# # caller on the first unrecognised argument we encounter. +# while test $# -gt 0; do +# opt=$1; shift +# case $opt in +# --silent|-s) opt_silent=: +# args_changed=: +# ;; +# # Separate non-argument short options: +# -s*) func_split_short_opt "$_G_opt" +# set dummy "$func_split_short_opt_name" \ +# "-$func_split_short_opt_arg" ${1+"$@"} +# shift +# args_changed=: +# ;; +# *) # Make sure the first unrecognised option "$_G_opt" +# # is added back to "$@", we could need that later +# # if $args_changed is true. +# set dummy "$_G_opt" ${1+"$@"}; shift; break ;; +# esac +# done +# +# if $args_changed; then +# func_quote_for_eval ${1+"$@"} +# my_silent_option_result=$func_quote_for_eval_result +# fi +# +# $args_changed +# } +# func_add_hook func_parse_options my_silent_option +# +# +# my_option_validation () +# { +# $debug_cmd +# +# $opt_silent && $opt_verbose && func_fatal_help "\ +# '--silent' and '--verbose' options are mutually exclusive." +# +# false +# } +# func_add_hook func_validate_options my_option_validation +# +# You'll also need to manually amend $usage_message to reflect the extra +# options you parse. It's preferable to append if you can, so that +# multiple option parsing hooks can be added safely. + + +# func_options_finish [ARG]... +# ---------------------------- +# Finishing the option parse loop (call 'func_options' hooks ATM). +func_options_finish () +{ + $debug_cmd + + _G_func_options_finish_exit=false + if func_run_hooks func_options ${1+"$@"}; then + func_options_finish_result=$func_run_hooks_result + _G_func_options_finish_exit=: + fi + + $_G_func_options_finish_exit +} + + +# func_options [ARG]... +# --------------------- +# All the functions called inside func_options are hookable. See the +# individual implementations for details. +func_hookable func_options +func_options () +{ + $debug_cmd + + _G_rc_options=false + + for my_func in options_prep parse_options validate_options options_finish + do + if eval func_$my_func '${1+"$@"}'; then + eval _G_res_var='$'"func_${my_func}_result" + eval set dummy "$_G_res_var" ; shift + _G_rc_options=: + fi + done + + # Save modified positional parameters for caller. As a top-level + # options-parser function we always need to set the 'func_options_result' + # variable (regardless the $_G_rc_options value). + if $_G_rc_options; then + func_options_result=$_G_res_var + else + func_quote_for_eval ${1+"$@"} + func_options_result=$func_quote_for_eval_result + fi + + $_G_rc_options +} + + +# func_options_prep [ARG]... +# -------------------------- +# All initialisations required before starting the option parse loop. +# Note that when calling hook functions, we pass through the list of +# positional parameters. If a hook function modifies that list, and +# needs to propagate that back to rest of this script, then the complete +# modified list must be put in 'func_run_hooks_result' before +# returning $EXIT_SUCCESS (otherwise $EXIT_FAILURE is returned). +func_hookable func_options_prep +func_options_prep () +{ + $debug_cmd + + # Option defaults: + opt_verbose=false + opt_warning_types= + + _G_rc_options_prep=false + if func_run_hooks func_options_prep ${1+"$@"}; then + _G_rc_options_prep=: + # save modified positional parameters for caller + func_options_prep_result=$func_run_hooks_result + fi + + $_G_rc_options_prep +} + + +# func_parse_options [ARG]... +# --------------------------- +# The main option parsing loop. +func_hookable func_parse_options +func_parse_options () +{ + $debug_cmd + + func_parse_options_result= + + _G_rc_parse_options=false + # this just eases exit handling + while test $# -gt 0; do + # Defer to hook functions for initial option parsing, so they + # get priority in the event of reusing an option name. + if func_run_hooks func_parse_options ${1+"$@"}; then + eval set dummy "$func_run_hooks_result"; shift + _G_rc_parse_options=: + fi + + # Break out of the loop if we already parsed every option. + test $# -gt 0 || break + + _G_match_parse_options=: + _G_opt=$1 + shift + case $_G_opt in + --debug|-x) debug_cmd='set -x' + func_echo "enabling shell trace mode" + $debug_cmd + ;; + + --no-warnings|--no-warning|--no-warn) + set dummy --warnings none ${1+"$@"} + shift + ;; + + --warnings|--warning|-W) + if test $# = 0 && func_missing_arg $_G_opt; then + _G_rc_parse_options=: + break + fi + case " $warning_categories $1" in + *" $1 "*) + # trailing space prevents matching last $1 above + func_append_uniq opt_warning_types " $1" + ;; + *all) + opt_warning_types=$warning_categories + ;; + *none) + opt_warning_types=none + warning_func=: + ;; + *error) + opt_warning_types=$warning_categories + warning_func=func_fatal_error + ;; + *) + func_fatal_error \ + "unsupported warning category: '$1'" + ;; + esac + shift + ;; + + --verbose|-v) opt_verbose=: ;; + --version) func_version ;; + -\?|-h) func_usage ;; + --help) func_help ;; + + # Separate optargs to long options (plugins may need this): + --*=*) func_split_equals "$_G_opt" + set dummy "$func_split_equals_lhs" \ + "$func_split_equals_rhs" ${1+"$@"} + shift + ;; + + # Separate optargs to short options: + -W*) + func_split_short_opt "$_G_opt" + set dummy "$func_split_short_opt_name" \ + "$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + # Separate non-argument short options: + -\?*|-h*|-v*|-x*) + func_split_short_opt "$_G_opt" + set dummy "$func_split_short_opt_name" \ + "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + --) _G_rc_parse_options=: ; break ;; + -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; + *) set dummy "$_G_opt" ${1+"$@"}; shift + _G_match_parse_options=false + break + ;; + esac + + $_G_match_parse_options && _G_rc_parse_options=: + done + + + if $_G_rc_parse_options; then + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + func_parse_options_result=$func_quote_for_eval_result + fi + + $_G_rc_parse_options +} + + +# func_validate_options [ARG]... +# ------------------------------ +# Perform any sanity checks on option settings and/or unconsumed +# arguments. +func_hookable func_validate_options +func_validate_options () +{ + $debug_cmd + + _G_rc_validate_options=false + + # Display all warnings if -W was not given. + test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" + + if func_run_hooks func_validate_options ${1+"$@"}; then + # save modified positional parameters for caller + func_validate_options_result=$func_run_hooks_result + _G_rc_validate_options=: + fi + + # Bail if the options were screwed! + $exit_cmd $EXIT_FAILURE + + $_G_rc_validate_options +} + + + +## ----------------- ## +## Helper functions. ## +## ----------------- ## + +# This section contains the helper functions used by the rest of the +# hookable option parser framework in ascii-betical order. + + +# func_fatal_help ARG... +# ---------------------- +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + $debug_cmd + + eval \$ECHO \""Usage: $usage"\" + eval \$ECHO \""$fatal_help"\" + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + + +# func_help +# --------- +# Echo long help message to standard output and exit. +func_help () +{ + $debug_cmd + + func_usage_message + $ECHO "$long_help_message" + exit 0 +} + + +# func_missing_arg ARGNAME +# ------------------------ +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + $debug_cmd + + func_error "Missing argument for '$1'." + exit_cmd=exit +} + + +# func_split_equals STRING +# ------------------------ +# Set func_split_equals_lhs and func_split_equals_rhs shell variables after +# splitting STRING at the '=' sign. +test -z "$_G_HAVE_XSI_OPS" \ + && (eval 'x=a/b/c; + test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ + && _G_HAVE_XSI_OPS=yes + +if test yes = "$_G_HAVE_XSI_OPS" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_split_equals () + { + $debug_cmd + + func_split_equals_lhs=${1%%=*} + func_split_equals_rhs=${1#*=} + test "x$func_split_equals_lhs" = "x$1" \ + && func_split_equals_rhs= + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_split_equals () + { + $debug_cmd + + func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` + func_split_equals_rhs= + test "x$func_split_equals_lhs" = "x$1" \ + || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` + } +fi #func_split_equals + + +# func_split_short_opt SHORTOPT +# ----------------------------- +# Set func_split_short_opt_name and func_split_short_opt_arg shell +# variables after splitting SHORTOPT after the 2nd character. +if test yes = "$_G_HAVE_XSI_OPS" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_split_short_opt () + { + $debug_cmd + + func_split_short_opt_arg=${1#??} + func_split_short_opt_name=${1%"$func_split_short_opt_arg"} + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_split_short_opt () + { + $debug_cmd + + func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` + func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` + } +fi #func_split_short_opt + + +# func_usage +# ---------- +# Echo short help message to standard output and exit. +func_usage () +{ + $debug_cmd + + func_usage_message + $ECHO "Run '$progname --help |${PAGER-more}' for full usage" + exit 0 +} + + +# func_usage_message +# ------------------ +# Echo short help message to standard output. +func_usage_message () +{ + $debug_cmd + + eval \$ECHO \""Usage: $usage"\" + echo + $SED -n 's|^# || + /^Written by/{ + x;p;x + } + h + /^Written by/q' < "$progpath" + echo + eval \$ECHO \""$usage_message"\" +} + + +# func_version +# ------------ +# Echo version message to standard output and exit. +func_version () +{ + $debug_cmd + + printf '%s\n' "$progname $scriptversion" + $SED -n ' + /(C)/!b go + :more + /\./!{ + N + s|\n# | | + b more + } + :go + /^# Written by /,/# warranty; / { + s|^# || + s|^# *$|| + s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| + p + } + /^# Written by / { + s|^# || + p + } + /^warranty; /q' < "$progpath" + + exit $? +} + + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: + +# Set a version string. +scriptversion='(GNU libtool) 2.4.6' + + +# func_echo ARG... +# ---------------- +# Libtool also displays the current mode in messages, so override +# funclib.sh func_echo with this custom definition. +func_echo () +{ + $debug_cmd + + _G_message=$* + + func_echo_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_IFS + $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" + done + IFS=$func_echo_IFS +} + + +# func_warning ARG... +# ------------------- +# Libtool warnings are not categorized, so override funclib.sh +# func_warning with this simpler definition. +func_warning () +{ + $debug_cmd + + $warning_func ${1+"$@"} +} + + +## ---------------- ## +## Options parsing. ## +## ---------------- ## + +# Hook in the functions to make sure our own options are parsed during +# the option parsing loop. + +usage='$progpath [OPTION]... [MODE-ARG]...' + +# Short help message in response to '-h'. +usage_message="Options: + --config show all configuration variables + --debug enable verbose shell tracing + -n, --dry-run display commands without modifying any files + --features display basic configuration information and exit + --mode=MODE use operation mode MODE + --no-warnings equivalent to '-Wnone' + --preserve-dup-deps don't remove duplicate dependency libraries + --quiet, --silent don't print informational messages + --tag=TAG use configuration variables from tag TAG + -v, --verbose print more informational messages than default + --version print version information + -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] + -h, --help, --help-all print short, long, or detailed help message +" + +# Additional text appended to 'usage_message' in response to '--help'. +func_help () +{ + $debug_cmd + + func_usage_message + $ECHO "$long_help_message + +MODE must be one of the following: + + clean remove files from the build directory + compile compile a source file into a libtool object + execute automatically set library path, then run a program + finish complete the installation of libtool libraries + install install libraries or executables + link create a library or an executable + uninstall remove libraries from an installed directory + +MODE-ARGS vary depending on the MODE. When passed as first option, +'--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. +Try '$progname --help --mode=MODE' for a more detailed description of MODE. + +When reporting a bug, please describe a test case to reproduce it and +include the following information: + + host-triplet: $host + shell: $SHELL + compiler: $LTCC + compiler flags: $LTCFLAGS + linker: $LD (gnu? $with_gnu_ld) + version: $progname $scriptversion Debian-2.4.6-15 + automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` + autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` + +Report bugs to . +GNU libtool home page: . +General help using GNU software: ." + exit 0 +} + + +# func_lo2o OBJECT-NAME +# --------------------- +# Transform OBJECT-NAME from a '.lo' suffix to the platform specific +# object suffix. + +lo2o=s/\\.lo\$/.$objext/ +o2lo=s/\\.$objext\$/.lo/ + +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_lo2o () + { + case $1 in + *.lo) func_lo2o_result=${1%.lo}.$objext ;; + * ) func_lo2o_result=$1 ;; + esac + }' + + # func_xform LIBOBJ-OR-SOURCE + # --------------------------- + # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) + # suffix to a '.lo' libtool-object suffix. + eval 'func_xform () + { + func_xform_result=${1%.*}.lo + }' +else + # ...otherwise fall back to using sed. + func_lo2o () + { + func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` + } + + func_xform () + { + func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` + } +fi + + +# func_fatal_configuration ARG... +# ------------------------------- +# Echo program name prefixed message to standard error, followed by +# a configuration failure hint, and exit. +func_fatal_configuration () +{ + func__fatal_error ${1+"$@"} \ + "See the $PACKAGE documentation for more information." \ + "Fatal configuration error." +} + + +# func_config +# ----------- +# Display the configuration for all the tags in this script. +func_config () +{ + re_begincf='^# ### BEGIN LIBTOOL' + re_endcf='^# ### END LIBTOOL' + + # Default configuration. + $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" + + # Now print the configurations for the tags. + for tagname in $taglist; do + $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" + done + + exit $? +} + + +# func_features +# ------------- +# Display the features supported by this script. +func_features () +{ + echo "host: $host" + if test yes = "$build_libtool_libs"; then + echo "enable shared libraries" + else + echo "disable shared libraries" + fi + if test yes = "$build_old_libs"; then + echo "enable static libraries" + else + echo "disable static libraries" + fi + + exit $? +} + + +# func_enable_tag TAGNAME +# ----------------------- +# Verify that TAGNAME is valid, and either flag an error and exit, or +# enable the TAGNAME tag. We also add TAGNAME to the global $taglist +# variable here. +func_enable_tag () +{ + # Global variable: + tagname=$1 + + re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" + re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" + sed_extractcf=/$re_begincf/,/$re_endcf/p + + # Validate tagname. + case $tagname in + *[!-_A-Za-z0-9,/]*) + func_fatal_error "invalid tag name: $tagname" + ;; + esac + + # Don't test for the "default" C tag, as we know it's + # there but not specially marked. + case $tagname in + CC) ;; + *) + if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then + taglist="$taglist $tagname" + + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + + +# func_check_version_match +# ------------------------ +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + fi + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +# libtool_options_prep [ARG]... +# ----------------------------- +# Preparation for options parsed by libtool. +libtool_options_prep () +{ + $debug_mode + + # Option defaults: + opt_config=false + opt_dlopen= + opt_dry_run=false + opt_help=false + opt_mode= + opt_preserve_dup_deps=false + opt_quiet=false + + nonopt= + preserve_args= + + _G_rc_lt_options_prep=: + + # Shorthand for --mode=foo, only valid as the first argument + case $1 in + clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; + compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; + execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; + finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; + install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; + link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; + uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift + ;; + *) + _G_rc_lt_options_prep=false + ;; + esac + + if $_G_rc_lt_options_prep; then + # Pass back the list of options. + func_quote_for_eval ${1+"$@"} + libtool_options_prep_result=$func_quote_for_eval_result + fi + + $_G_rc_lt_options_prep +} +func_add_hook func_options_prep libtool_options_prep + + +# libtool_parse_options [ARG]... +# --------------------------------- +# Provide handling for libtool specific options. +libtool_parse_options () +{ + $debug_cmd + + _G_rc_lt_parse_options=false + + # Perform our own loop to consume as many options as possible in + # each iteration. + while test $# -gt 0; do + _G_match_lt_parse_options=: + _G_opt=$1 + shift + case $_G_opt in + --dry-run|--dryrun|-n) + opt_dry_run=: + ;; + + --config) func_config ;; + + --dlopen|-dlopen) + opt_dlopen="${opt_dlopen+$opt_dlopen +}$1" + shift + ;; + + --preserve-dup-deps) + opt_preserve_dup_deps=: ;; + + --features) func_features ;; + + --finish) set dummy --mode finish ${1+"$@"}; shift ;; + + --help) opt_help=: ;; + + --help-all) opt_help=': help-all' ;; + + --mode) test $# = 0 && func_missing_arg $_G_opt && break + opt_mode=$1 + case $1 in + # Valid mode arguments: + clean|compile|execute|finish|install|link|relink|uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $_G_opt" + exit_cmd=exit + break + ;; + esac + shift + ;; + + --no-silent|--no-quiet) + opt_quiet=false + func_append preserve_args " $_G_opt" + ;; + + --no-warnings|--no-warning|--no-warn) + opt_warning=false + func_append preserve_args " $_G_opt" + ;; + + --no-verbose) + opt_verbose=false + func_append preserve_args " $_G_opt" + ;; + + --silent|--quiet) + opt_quiet=: + opt_verbose=false + func_append preserve_args " $_G_opt" + ;; + + --tag) test $# = 0 && func_missing_arg $_G_opt && break + opt_tag=$1 + func_append preserve_args " $_G_opt $1" + func_enable_tag "$1" + shift + ;; + + --verbose|-v) opt_quiet=false + opt_verbose=: + func_append preserve_args " $_G_opt" + ;; + + # An option not handled by this hook function: + *) set dummy "$_G_opt" ${1+"$@"} ; shift + _G_match_lt_parse_options=false + break + ;; + esac + $_G_match_lt_parse_options && _G_rc_lt_parse_options=: + done + + if $_G_rc_lt_parse_options; then + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + libtool_parse_options_result=$func_quote_for_eval_result + fi + + $_G_rc_lt_parse_options +} +func_add_hook func_parse_options libtool_parse_options + + + +# libtool_validate_options [ARG]... +# --------------------------------- +# Perform any sanity checks on option settings and/or unconsumed +# arguments. +libtool_validate_options () +{ + # save first non-option argument + if test 0 -lt $#; then + nonopt=$1 + shift + fi + + # preserve --debug + test : = "$debug_cmd" || func_append preserve_args " --debug" + + case $host in + # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 + # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 + *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + ;; + esac + + $opt_help || { + # Sanity checks first: + func_check_version_match + + test yes != "$build_libtool_libs" \ + && test yes != "$build_old_libs" \ + && func_fatal_configuration "not configured to build any kind of library" + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$opt_dlopen" && test execute != "$opt_mode"; then + func_error "unrecognized option '-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help=$help + help="Try '$progname --help --mode=$opt_mode' for more information." + } + + # Pass back the unparsed argument list + func_quote_for_eval ${1+"$@"} + libtool_validate_options_result=$func_quote_for_eval_result +} +func_add_hook func_validate_options libtool_validate_options + + +# Process options as early as possible so that --help and --version +# can return quickly. +func_options ${1+"$@"} +eval set dummy "$func_options_result"; shift + + + +## ----------- ## +## Main. ## +## ----------- ## + +magic='%%%MAGIC variable%%%' +magic_exe='%%%MAGIC EXE variable%%%' + +# Global variables. +extracted_archives= +extracted_serial=0 + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# func_generated_by_libtool +# True iff stdin has been generated by Libtool. This function is only +# a basic sanity check; it will hardly flush out determined imposters. +func_generated_by_libtool_p () +{ + $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + +# func_lalib_p file +# True iff FILE is a libtool '.la' library or '.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p +} + +# func_lalib_unsafe_p file +# True iff FILE is a libtool '.la' library or '.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if 'file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case $lalib_p_line in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test yes = "$lalib_p" +} + +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + test -f "$1" && + $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p +} + +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} + +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper +} + +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} + + +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $debug_cmd + + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$sp$nl + eval cmd=\"$cmd\" + IFS=$save_ifs + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# 'FILE.' does not work on cygwin managed mounts. +func_source () +{ + $debug_cmd + + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_resolve_sysroot PATH +# Replace a leading = in PATH with a sysroot. Store the result into +# func_resolve_sysroot_result +func_resolve_sysroot () +{ + func_resolve_sysroot_result=$1 + case $func_resolve_sysroot_result in + =*) + func_stripname '=' '' "$func_resolve_sysroot_result" + func_resolve_sysroot_result=$lt_sysroot$func_stripname_result + ;; + esac +} + +# func_replace_sysroot PATH +# If PATH begins with the sysroot, replace it with = and +# store the result into func_replace_sysroot_result. +func_replace_sysroot () +{ + case $lt_sysroot:$1 in + ?*:"$lt_sysroot"*) + func_stripname "$lt_sysroot" '' "$1" + func_replace_sysroot_result='='$func_stripname_result + ;; + *) + # Including no sysroot. + func_replace_sysroot_result=$1 + ;; + esac +} + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + $debug_cmd + + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case "$@ " in + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with '--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=$1 + if test yes = "$build_libtool_libs"; then + write_lobj=\'$2\' + else + write_lobj=none + fi + + if test yes = "$build_old_libs"; then + write_oldobj=\'$3\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T </dev/null` + if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then + func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | + $SED -e "$sed_naive_backslashify"` + else + func_convert_core_file_wine_to_w32_result= + fi + fi +} +# end: func_convert_core_file_wine_to_w32 + + +# func_convert_core_path_wine_to_w32 ARG +# Helper function used by path conversion functions when $build is *nix, and +# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly +# configured wine environment available, with the winepath program in $build's +# $PATH. Assumes ARG has no leading or trailing path separator characters. +# +# ARG is path to be converted from $build format to win32. +# Result is available in $func_convert_core_path_wine_to_w32_result. +# Unconvertible file (directory) names in ARG are skipped; if no directory names +# are convertible, then the result may be empty. +func_convert_core_path_wine_to_w32 () +{ + $debug_cmd + + # unfortunately, winepath doesn't convert paths, only file names + func_convert_core_path_wine_to_w32_result= + if test -n "$1"; then + oldIFS=$IFS + IFS=: + for func_convert_core_path_wine_to_w32_f in $1; do + IFS=$oldIFS + func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" + if test -n "$func_convert_core_file_wine_to_w32_result"; then + if test -z "$func_convert_core_path_wine_to_w32_result"; then + func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result + else + func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" + fi + fi + done + IFS=$oldIFS + fi +} +# end: func_convert_core_path_wine_to_w32 + + +# func_cygpath ARGS... +# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when +# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) +# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or +# (2), returns the Cygwin file name or path in func_cygpath_result (input +# file name or path is assumed to be in w32 format, as previously converted +# from $build's *nix or MSYS format). In case (3), returns the w32 file name +# or path in func_cygpath_result (input file name or path is assumed to be in +# Cygwin format). Returns an empty string on error. +# +# ARGS are passed to cygpath, with the last one being the file name or path to +# be converted. +# +# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH +# environment variable; do not put it in $PATH. +func_cygpath () +{ + $debug_cmd + + if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then + func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` + if test "$?" -ne 0; then + # on failure, ensure result is empty + func_cygpath_result= + fi + else + func_cygpath_result= + func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" + fi +} +#end: func_cygpath + + +# func_convert_core_msys_to_w32 ARG +# Convert file name or path ARG from MSYS format to w32 format. Return +# result in func_convert_core_msys_to_w32_result. +func_convert_core_msys_to_w32 () +{ + $debug_cmd + + # awkward: cmd appends spaces to result + func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | + $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` +} +#end: func_convert_core_msys_to_w32 + + +# func_convert_file_check ARG1 ARG2 +# Verify that ARG1 (a file name in $build format) was converted to $host +# format in ARG2. Otherwise, emit an error message, but continue (resetting +# func_to_host_file_result to ARG1). +func_convert_file_check () +{ + $debug_cmd + + if test -z "$2" && test -n "$1"; then + func_error "Could not determine host file name corresponding to" + func_error " '$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_file_result=$1 + fi +} +# end func_convert_file_check + + +# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH +# Verify that FROM_PATH (a path in $build format) was converted to $host +# format in TO_PATH. Otherwise, emit an error message, but continue, resetting +# func_to_host_file_result to a simplistic fallback value (see below). +func_convert_path_check () +{ + $debug_cmd + + if test -z "$4" && test -n "$3"; then + func_error "Could not determine the host path corresponding to" + func_error " '$3'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This is a deliberately simplistic "conversion" and + # should not be "improved". See libtool.info. + if test "x$1" != "x$2"; then + lt_replace_pathsep_chars="s|$1|$2|g" + func_to_host_path_result=`echo "$3" | + $SED -e "$lt_replace_pathsep_chars"` + else + func_to_host_path_result=$3 + fi + fi +} +# end func_convert_path_check + + +# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG +# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT +# and appending REPL if ORIG matches BACKPAT. +func_convert_path_front_back_pathsep () +{ + $debug_cmd + + case $4 in + $1 ) func_to_host_path_result=$3$func_to_host_path_result + ;; + esac + case $4 in + $2 ) func_append func_to_host_path_result "$3" + ;; + esac +} +# end func_convert_path_front_back_pathsep + + +################################################## +# $build to $host FILE NAME CONVERSION FUNCTIONS # +################################################## +# invoked via '$to_host_file_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# Result will be available in $func_to_host_file_result. + + +# func_to_host_file ARG +# Converts the file name ARG from $build format to $host format. Return result +# in func_to_host_file_result. +func_to_host_file () +{ + $debug_cmd + + $to_host_file_cmd "$1" +} +# end func_to_host_file + + +# func_to_tool_file ARG LAZY +# converts the file name ARG from $build format to toolchain format. Return +# result in func_to_tool_file_result. If the conversion in use is listed +# in (the comma separated) LAZY, no conversion takes place. +func_to_tool_file () +{ + $debug_cmd + + case ,$2, in + *,"$to_tool_file_cmd",*) + func_to_tool_file_result=$1 + ;; + *) + $to_tool_file_cmd "$1" + func_to_tool_file_result=$func_to_host_file_result + ;; + esac +} +# end func_to_tool_file + + +# func_convert_file_noop ARG +# Copy ARG to func_to_host_file_result. +func_convert_file_noop () +{ + func_to_host_file_result=$1 +} +# end func_convert_file_noop + + +# func_convert_file_msys_to_w32 ARG +# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_file_result. +func_convert_file_msys_to_w32 () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_to_host_file_result=$func_convert_core_msys_to_w32_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_w32 + + +# func_convert_file_cygwin_to_w32 ARG +# Convert file name ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_file_cygwin_to_w32 () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + # because $build is cygwin, we call "the" cygpath in $PATH; no need to use + # LT_CYGPATH in this case. + func_to_host_file_result=`cygpath -m "$1"` + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_cygwin_to_w32 + + +# func_convert_file_nix_to_w32 ARG +# Convert file name ARG from *nix to w32 format. Requires a wine environment +# and a working winepath. Returns result in func_to_host_file_result. +func_convert_file_nix_to_w32 () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + func_convert_core_file_wine_to_w32 "$1" + func_to_host_file_result=$func_convert_core_file_wine_to_w32_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_w32 + + +# func_convert_file_msys_to_cygwin ARG +# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_file_msys_to_cygwin () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_cygpath -u "$func_convert_core_msys_to_w32_result" + func_to_host_file_result=$func_cygpath_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_cygwin + + +# func_convert_file_nix_to_cygwin ARG +# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed +# in a wine environment, working winepath, and LT_CYGPATH set. Returns result +# in func_to_host_file_result. +func_convert_file_nix_to_cygwin () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. + func_convert_core_file_wine_to_w32 "$1" + func_cygpath -u "$func_convert_core_file_wine_to_w32_result" + func_to_host_file_result=$func_cygpath_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_cygwin + + +############################################# +# $build to $host PATH CONVERSION FUNCTIONS # +############################################# +# invoked via '$to_host_path_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# The result will be available in $func_to_host_path_result. +# +# Path separators are also converted from $build format to $host format. If +# ARG begins or ends with a path separator character, it is preserved (but +# converted to $host format) on output. +# +# All path conversion functions are named using the following convention: +# file name conversion function : func_convert_file_X_to_Y () +# path conversion function : func_convert_path_X_to_Y () +# where, for any given $build/$host combination the 'X_to_Y' value is the +# same. If conversion functions are added for new $build/$host combinations, +# the two new functions must follow this pattern, or func_init_to_host_path_cmd +# will break. + + +# func_init_to_host_path_cmd +# Ensures that function "pointer" variable $to_host_path_cmd is set to the +# appropriate value, based on the value of $to_host_file_cmd. +to_host_path_cmd= +func_init_to_host_path_cmd () +{ + $debug_cmd + + if test -z "$to_host_path_cmd"; then + func_stripname 'func_convert_file_' '' "$to_host_file_cmd" + to_host_path_cmd=func_convert_path_$func_stripname_result + fi +} + + +# func_to_host_path ARG +# Converts the path ARG from $build format to $host format. Return result +# in func_to_host_path_result. +func_to_host_path () +{ + $debug_cmd + + func_init_to_host_path_cmd + $to_host_path_cmd "$1" +} +# end func_to_host_path + + +# func_convert_path_noop ARG +# Copy ARG to func_to_host_path_result. +func_convert_path_noop () +{ + func_to_host_path_result=$1 +} +# end func_convert_path_noop + + +# func_convert_path_msys_to_w32 ARG +# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_path_result. +func_convert_path_msys_to_w32 () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # Remove leading and trailing path separator characters from ARG. MSYS + # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; + # and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result=$func_convert_core_msys_to_w32_result + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_msys_to_w32 + + +# func_convert_path_cygwin_to_w32 ARG +# Convert path ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_path_cygwin_to_w32 () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_cygwin_to_w32 + + +# func_convert_path_nix_to_w32 ARG +# Convert path ARG from *nix to w32 format. Requires a wine environment and +# a working winepath. Returns result in func_to_host_file_result. +func_convert_path_nix_to_w32 () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result=$func_convert_core_path_wine_to_w32_result + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_nix_to_w32 + + +# func_convert_path_msys_to_cygwin ARG +# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_path_msys_to_cygwin () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_msys_to_w32_result" + func_to_host_path_result=$func_cygpath_result + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_msys_to_cygwin + + +# func_convert_path_nix_to_cygwin ARG +# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a +# a wine environment, working winepath, and LT_CYGPATH set. Returns result in +# func_to_host_file_result. +func_convert_path_nix_to_cygwin () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" + func_to_host_path_result=$func_cygpath_result + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_nix_to_cygwin + + +# func_dll_def_p FILE +# True iff FILE is a Windows DLL '.def' file. +# Keep in sync with _LT_DLL_DEF_P in libtool.m4 +func_dll_def_p () +{ + $debug_cmd + + func_dll_def_p_tmp=`$SED -n \ + -e 's/^[ ]*//' \ + -e '/^\(;.*\)*$/d' \ + -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ + -e q \ + "$1"` + test DEF = "$func_dll_def_p_tmp" +} + + +# func_mode_compile arg... +func_mode_compile () +{ + $debug_cmd + + # Get the compilation command and the source file. + base_compile= + srcfile=$nonopt # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + pie_flag= + + for arg + do + case $arg_mode in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg=$arg + arg_mode=normal + ;; + + target ) + libobj=$arg + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + test -n "$libobj" && \ + func_fatal_error "you cannot specify '-o' more than once" + arg_mode=target + continue + ;; + + -pie | -fpie | -fPIE) + func_append pie_flag " $arg" + continue + ;; + + -shared | -static | -prefer-pic | -prefer-non-pic) + func_append later " $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + lastarg= + save_ifs=$IFS; IFS=, + for arg in $args; do + IFS=$save_ifs + func_append_quoted lastarg "$arg" + done + IFS=$save_ifs + func_stripname ' ' '' "$lastarg" + lastarg=$func_stripname_result + + # Add the arguments to base_compile. + func_append base_compile " $lastarg" + continue + ;; + + *) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg=$srcfile + srcfile=$arg + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + func_append_quoted base_compile "$lastarg" + done # for arg + + case $arg_mode in + arg) + func_fatal_error "you must specify an argument for -Xcompile" + ;; + target) + func_fatal_error "you must specify a target with '-o'" + ;; + *) + # Get the name of the library object. + test -z "$libobj" && { + func_basename "$srcfile" + libobj=$func_basename_result + } + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + case $libobj in + *.[cCFSifmso] | \ + *.ada | *.adb | *.ads | *.asm | \ + *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ + *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) + func_xform "$libobj" + libobj=$func_xform_result + ;; + esac + + case $libobj in + *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; + *) + func_fatal_error "cannot determine name of library object from '$libobj'" + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -shared) + test yes = "$build_libtool_libs" \ + || func_fatal_configuration "cannot build a shared library" + build_old_libs=no + continue + ;; + + -static) + build_libtool_libs=no + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + func_quote_for_eval "$libobj" + test "X$libobj" != "X$func_quote_for_eval_result" \ + && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && func_warning "libobj name '$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname=$func_basename_result + xdir=$func_dirname_result + lobj=$xdir$objdir/$objname + + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" + + # Delete any leftover library objects. + if test yes = "$build_old_libs"; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2* | cegcc*) + pic_mode=default + ;; + esac + if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test no = "$compiler_c_o"; then + output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext + lockfile=$output_obj.lock + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test yes = "$need_locks"; then + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + elif test warn = "$need_locks"; then + if test -f "$lockfile"; then + $ECHO "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support '-c' and '-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + func_append removelist " $output_obj" + $ECHO "$srcfile" > "$lockfile" + fi + + $opt_dry_run || $RM $removelist + func_append removelist " $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + + func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 + srcfile=$func_to_tool_file_result + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result + + # Only build a PIC object if we are building libtool libraries. + if test yes = "$build_libtool_libs"; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test no != "$pic_mode"; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + func_mkdir_p "$xdir$objdir" + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + func_append command " -o $lobj" + fi + + func_show_eval_locale "$command" \ + 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' + + if test warn = "$need_locks" && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support '-c' and '-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + + # Allow error messages only from the first compilation. + if test yes = "$suppress_opt"; then + suppress_output=' >/dev/null 2>&1' + fi + fi + + # Only build a position-dependent object if we build old libraries. + if test yes = "$build_old_libs"; then + if test yes != "$pic_mode"; then + # Don't build PIC code + command="$base_compile $qsrcfile$pie_flag" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test yes = "$compiler_c_o"; then + func_append command " -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + func_append command "$suppress_output" + func_show_eval_locale "$command" \ + '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' + + if test warn = "$need_locks" && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support '-c' and '-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + fi + + $opt_dry_run || { + func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" + + # Unlock the critical section if it was locked + if test no != "$need_locks"; then + removelist=$lockfile + $RM "$lockfile" + fi + } + + exit $EXIT_SUCCESS +} + +$opt_help || { + test compile = "$opt_mode" && func_mode_compile ${1+"$@"} +} + +func_mode_help () +{ + # We need to display help for each of the modes. + case $opt_mode in + "") + # Generic help is extracted from the usage comments + # at the start of this file. + func_help + ;; + + clean) + $ECHO \ +"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + + compile) + $ECHO \ +"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to build PIC objects only + -prefer-non-pic try to build non-PIC objects only + -shared do not build a '.o' file suitable for static linking + -static only build a '.o' file suitable for static linking + -Wc,FLAG pass FLAG directly to the compiler + +COMPILE-COMMAND is a command to be used in creating a 'standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix '.c' with the +library object suffix, '.lo'." + ;; + + execute) + $ECHO \ +"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to '-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + + finish) + $ECHO \ +"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the '--dry-run' option if you just want to see what would be executed." + ;; + + install) + $ECHO \ +"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the 'install' or 'cp' program. + +The following components of INSTALL-COMMAND are treated specially: + + -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + + link) + $ECHO \ +"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -bindir BINDIR specify path to binaries directory (for systems where + libraries must be found in the PATH setting at runtime) + -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE use a list of object files found in FILE to specify objects + -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface + -Wc,FLAG + -Xcompiler FLAG pass linker-specific FLAG directly to the compiler + -Wl,FLAG + -Xlinker FLAG pass linker-specific FLAG directly to the linker + -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) + +All other options (arguments beginning with '-') are ignored. + +Every other argument is treated as a filename. Files ending in '.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in '.la', then a libtool library is created, +only library objects ('.lo' files) may be specified, and '-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created +using 'ar' and 'ranlib', or on Windows using 'lib'. + +If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file +is created, otherwise an executable program is created." + ;; + + uninstall) + $ECHO \ +"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + + *) + func_fatal_help "invalid operation mode '$opt_mode'" + ;; + esac + + echo + $ECHO "Try '$progname --help' for more information about other modes." +} + +# Now that we've collected a possible --mode arg, show help if necessary +if $opt_help; then + if test : = "$opt_help"; then + func_mode_help + else + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + func_mode_help + done + } | $SED -n '1p; 2,$s/^Usage:/ or: /p' + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + echo + func_mode_help + done + } | + $SED '1d + /^When reporting/,/^Report/{ + H + d + } + $x + /information about other modes/d + /more detailed .*MODE/d + s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' + fi + exit $? +fi + + +# func_mode_execute arg... +func_mode_execute () +{ + $debug_cmd + + # The first argument is the command name. + cmd=$nonopt + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" + + # Handle -dlopen flags immediately. + for file in $opt_dlopen; do + test -f "$file" \ + || func_fatal_help "'$file' is not a file" + + dir= + case $file in + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "'$lib' is not a valid libtool archive" + + # Read the libtool library. + dlname= + library_names= + func_source "$file" + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "'$file' was not linked with '-export-dynamic'" + continue + fi + + func_dirname "$file" "" "." + dir=$func_dirname_result + + if test -f "$dir/$objdir/$dlname"; then + func_append dir "/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" + fi + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir=$func_dirname_result + ;; + + *) + func_warning "'-dlopen' is ignored for non-libtool libraries and objects" + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir=$absdir + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic=$magic + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -* | *.la | *.lo ) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file=$progdir/$program + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file=$progdir/$program + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_append_quoted args "$file" + done + + if $opt_dry_run; then + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" + echo "export $shlibpath_var" + fi + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + else + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done + + # Now prepare to actually exec the command. + exec_cmd=\$cmd$args + fi +} + +test execute = "$opt_mode" && func_mode_execute ${1+"$@"} + + +# func_mode_finish arg... +func_mode_finish () +{ + $debug_cmd + + libs= + libdirs= + admincmds= + + for opt in "$nonopt" ${1+"$@"} + do + if test -d "$opt"; then + func_append libdirs " $opt" + + elif test -f "$opt"; then + if func_lalib_unsafe_p "$opt"; then + func_append libs " $opt" + else + func_warning "'$opt' is not a valid libtool archive" + fi + + else + func_fatal_error "invalid argument '$opt'" + fi + done + + if test -n "$libs"; then + if test -n "$lt_sysroot"; then + sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` + sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" + else + sysroot_cmd= + fi + + # Remove sysroot references + if $opt_dry_run; then + for lib in $libs; do + echo "removing references to $lt_sysroot and '=' prefixes from $lib" + done + else + tmpdir=`func_mktempdir` + for lib in $libs; do + $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ + > $tmpdir/tmp-la + mv -f $tmpdir/tmp-la $lib + done + ${RM}r "$tmpdir" + fi + fi + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || func_append admincmds " + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + $opt_quiet && exit $EXIT_SUCCESS + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + echo "----------------------------------------------------------------------" + echo "Libraries have been installed in:" + for libdir in $libdirs; do + $ECHO " $libdir" + done + echo + echo "If you ever happen to want to link against installed libraries" + echo "in a given directory, LIBDIR, you must either use libtool, and" + echo "specify the full pathname of the library, or use the '-LLIBDIR'" + echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + echo " - add LIBDIR to the '$shlibpath_var' environment variable" + echo " during execution" + fi + if test -n "$runpath_var"; then + echo " - add LIBDIR to the '$runpath_var' environment variable" + echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $ECHO " - use the '$flag' linker flag" + fi + if test -n "$admincmds"; then + $ECHO " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" + fi + echo + + echo "See any operating system documentation about shared libraries for" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" + echo "pages." + ;; + *) + echo "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + echo "----------------------------------------------------------------------" + fi + exit $EXIT_SUCCESS +} + +test finish = "$opt_mode" && func_mode_finish ${1+"$@"} + + +# func_mode_install arg... +func_mode_install () +{ + $debug_cmd + + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || + # Allow the use of GNU shtool's install command. + case $nonopt in *shtool*) :;; *) false;; esac + then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + func_append install_prog "$func_quote_for_eval_result" + install_shared_prog=$install_prog + case " $install_prog " in + *[\\\ /]cp\ *) install_cp=: ;; + *) install_cp=false ;; + esac + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=false + stripme= + no_mode=: + for arg + do + arg2= + if test -n "$dest"; then + func_append files " $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=: ;; + -f) + if $install_cp; then :; else + prev=$arg + fi + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + if test X-m = "X$prev" && test -n "$install_override_mode"; then + arg2=$install_override_mode + no_mode=false + fi + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + func_append install_prog " $func_quote_for_eval_result" + if test -n "$arg2"; then + func_quote_for_eval "$arg2" + fi + func_append install_shared_prog " $func_quote_for_eval_result" + done + + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" + + test -n "$prev" && \ + func_fatal_help "the '$prev' option requires an argument" + + if test -n "$install_override_mode" && $no_mode; then + if $install_cp; then :; else + func_quote_for_eval "$install_override_mode" + func_append install_shared_prog " -m $func_quote_for_eval_result" + fi + fi + + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" + fi + fi + + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=: + if $isdir; then + destdir=$dest + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir=$func_dirname_result + destname=$func_basename_result + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "'$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "'$destdir' must be an absolute directory name" + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic=$magic + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + func_append staticlibs " $file" + ;; + + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "'$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) func_append current_libdirs " $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) func_append future_libdirs " $libdir" ;; + esac + fi + + func_dirname "$file" "/" "" + dir=$func_dirname_result + func_append dir "$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + fi + + func_warning "relinking '$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname=$1 + shift + + srcname=$realname + test -n "$relink_command" && srcname=${realname}T + + # Install the shared library and build the symlinks. + func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme=$stripme + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme= + ;; + esac + ;; + os2*) + case $realname in + *_dll.a) + tstripme= + ;; + esac + ;; + esac + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try 'ln -sf' first, because the 'ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" + done + fi + + # Do each command in the postinstall commands. + lib=$destdir/$realname + func_execute_cmds "$postinstall_cmds" 'exit $?' + fi + + # Install the pseudo-library for information purposes. + func_basename "$file" + name=$func_basename_result + instname=$dir/${name}i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' + + # Maybe install the static library, too. + test -n "$old_library" && func_append staticlibs " $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile=$destdir/$destname + else + func_basename "$file" + destfile=$func_basename_result + destfile=$destdir/$destfile + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest=$destfile + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to '$destfile'" + ;; + esac + + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test yes = "$build_old_libs"; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile=$destdir/$destname + else + func_basename "$file" + destfile=$func_basename_result + destfile=$destdir/$destfile + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext= + case $file in + *.exe) + if test ! -f "$file"; then + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=.exe + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= + + func_source "$wrapper" + + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script '$wrapper'" + + finalize=: + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "'$lib' has not been installed in '$libdir'" + finalize=false + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test no = "$fast_install" && test -n "$relink_command"; then + $opt_dry_run || { + if $finalize; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file=$func_basename_result + outputname=$tmpdir/$file + # Replace the output file specification. + relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_quiet || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink '$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file=$outputname + else + func_warning "cannot relink '$file'" + fi + } + else + # Install the binary that we compiled earlier. + file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac + ;; + esac + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done + + for file in $staticlibs; do + func_basename "$file" + name=$func_basename_result + + # Set up the ranlib parameters. + oldlib=$destdir/$name + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $tool_oldlib" 'exit $?' + fi + + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done + + test -n "$future_libdirs" && \ + func_warning "remember to run '$progname --finish$future_libdirs'" + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} + +test install = "$opt_mode" && func_mode_install ${1+"$@"} + + +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $debug_cmd + + my_outputname=$1 + my_originator=$2 + my_pic_p=${3-false} + my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms=${my_outputname}S.c + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi + + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist=$output_objdir/$my_outputname.nm + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +#if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) +#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" +#endif + +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined __osf__ +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) + +/* External symbol declarations for the compiler. */\ +" + + if test yes = "$dlself"; then + func_verbose "generating symbol list for '$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_to_tool_file "$progfile" func_convert_file_msys_to_w32 + func_verbose "extracting global C symbols from '$func_to_tool_file_result'" + $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols=$output_objdir/$outputname.exp + $opt_dry_run || { + $RM $export_symbols + eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + } + else + $opt_dry_run || { + eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + } + fi + fi + + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from '$dlprefile'" + func_basename "$dlprefile" + name=$func_basename_result + case $host in + *cygwin* | *mingw* | *cegcc* ) + # if an import library, we need to obtain dlname + if func_win32_import_lib_p "$dlprefile"; then + func_tr_sh "$dlprefile" + eval "curr_lafile=\$libfile_$func_tr_sh_result" + dlprefile_dlbasename= + if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then + # Use subshell, to avoid clobbering current variable values + dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` + if test -n "$dlprefile_dlname"; then + func_basename "$dlprefile_dlname" + dlprefile_dlbasename=$func_basename_result + else + # no lafile. user explicitly requested -dlpreopen . + $sharedlib_from_linklib_cmd "$dlprefile" + dlprefile_dlbasename=$sharedlib_from_linklib_result + fi + fi + $opt_dry_run || { + if test -n "$dlprefile_dlbasename"; then + eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' + else + func_warning "Could not compute DLL name from $name" + eval '$ECHO ": $name " >> "$nlist"' + fi + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | + $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" + } + else # not an import lib + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + fi + ;; + *) + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + ;; + esac + done + + $opt_dry_run || { + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $MV "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if $GREP -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + $GREP -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' + else + echo '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi + + func_show_eval '$RM "${nlist}I"' + if test -n "$global_symbol_to_import"; then + eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' + fi + + echo >> "$output_objdir/$my_dlsyms" "\ + +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +extern LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[];\ +" + + if test -s "$nlist"I; then + echo >> "$output_objdir/$my_dlsyms" "\ +static void lt_syminit(void) +{ + LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; + for (; symbol->name; ++symbol) + {" + $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" + echo >> "$output_objdir/$my_dlsyms" "\ + } +}" + fi + echo >> "$output_objdir/$my_dlsyms" "\ +LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{ {\"$my_originator\", (void *) 0}," + + if test -s "$nlist"I; then + echo >> "$output_objdir/$my_dlsyms" "\ + {\"@INIT@\", (void *) <_syminit}," + fi + + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + echo >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + $my_pic_p && pic_flag_for_symtable=" $pic_flag" + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) func_append symtab_cflags " $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' + + # Transform the symbol file into the correct name. + symfileobj=$output_objdir/${my_outputname}S.$objext + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + else + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + fi + ;; + *) + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for '$my_dlsyms'" + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` + fi +} + +# func_cygming_gnu_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is a GNU/binutils-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_gnu_implib_p () +{ + $debug_cmd + + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` + test -n "$func_cygming_gnu_implib_tmp" +} + +# func_cygming_ms_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is an MS-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_ms_implib_p () +{ + $debug_cmd + + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` + test -n "$func_cygming_ms_implib_tmp" +} + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +# Despite the name, also deal with 64 bit binaries. +func_win32_libid () +{ + $debug_cmd + + win32_libid_type=unknown + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then + case $nm_interface in + "MS dumpbin") + if func_cygming_ms_implib_p "$1" || + func_cygming_gnu_implib_p "$1" + then + win32_nmres=import + else + win32_nmres= + fi + ;; + *) + func_to_tool_file "$1" func_convert_file_msys_to_w32 + win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | + $SED -n -e ' + 1,100{ + / I /{ + s|.*|import| + p + q + } + }'` + ;; + esac + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $ECHO "$win32_libid_type" +} + +# func_cygming_dll_for_implib ARG +# +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib () +{ + $debug_cmd + + sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` +} + +# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs +# +# The is the core of a fallback implementation of a +# platform-specific function to extract the name of the +# DLL associated with the specified import library LIBNAME. +# +# SECTION_NAME is either .idata$6 or .idata$7, depending +# on the platform and compiler that created the implib. +# +# Echos the name of the DLL associated with the +# specified import library. +func_cygming_dll_for_implib_fallback_core () +{ + $debug_cmd + + match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` + $OBJDUMP -s --section "$1" "$2" 2>/dev/null | + $SED '/^Contents of section '"$match_literal"':/{ + # Place marker at beginning of archive member dllname section + s/.*/====MARK====/ + p + d + } + # These lines can sometimes be longer than 43 characters, but + # are always uninteresting + /:[ ]*file format pe[i]\{,1\}-/d + /^In archive [^:]*:/d + # Ensure marker is printed + /^====MARK====/p + # Remove all lines with less than 43 characters + /^.\{43\}/!d + # From remaining lines, remove first 43 characters + s/^.\{43\}//' | + $SED -n ' + # Join marker and all lines until next marker into a single line + /^====MARK====/ b para + H + $ b para + b + :para + x + s/\n//g + # Remove the marker + s/^====MARK====// + # Remove trailing dots and whitespace + s/[\. \t]*$// + # Print + /./p' | + # we now have a list, one entry per line, of the stringified + # contents of the appropriate section of all members of the + # archive that possess that section. Heuristic: eliminate + # all those that have a first or second character that is + # a '.' (that is, objdump's representation of an unprintable + # character.) This should work for all archives with less than + # 0x302f exports -- but will fail for DLLs whose name actually + # begins with a literal '.' or a single character followed by + # a '.'. + # + # Of those that remain, print the first one. + $SED -e '/^\./d;/^.\./d;q' +} + +# func_cygming_dll_for_implib_fallback ARG +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# +# This fallback implementation is for use when $DLLTOOL +# does not support the --identify-strict option. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib_fallback () +{ + $debug_cmd + + if func_cygming_gnu_implib_p "$1"; then + # binutils import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` + elif func_cygming_ms_implib_p "$1"; then + # ms-generated import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` + else + # unknown + sharedlib_from_linklib_result= + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $debug_cmd + + f_ex_an_ar_dir=$1; shift + f_ex_an_ar_oldlib=$1 + if test yes = "$lock_old_archive_extraction"; then + lockfile=$f_ex_an_ar_oldlib.lock + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + fi + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ + 'stat=$?; rm -f "$lockfile"; exit $stat' + if test yes = "$lock_old_archive_extraction"; then + $opt_dry_run || rm -f "$lockfile" + fi + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $debug_cmd + + my_gentop=$1; shift + my_oldlibs=${1+"$@"} + my_oldobjs= + my_xlib= + my_xabs= + my_xdir= + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + func_basename "$my_xlib" + my_xlib=$func_basename_result + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir=$my_gentop/$my_xlib_u + + func_mkdir_p "$my_xdir" + + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + func_basename "$darwin_archive" + darwin_base_archive=$func_basename_result + darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches; do + func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" + $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" + cd "unfat-$$/$darwin_base_archive-$darwin_arch" + func_extract_an_archive "`pwd`" "$darwin_base_archive" + cd "$darwin_curdir" + $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` + done + + func_extract_archives_result=$my_oldobjs +} + + +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory where it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=${1-no} + + $ECHO "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE) $VERSION +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variables: + generated_by_libtool_version='$macro_version' + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$ECHO are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + file=\"\$0\"" + + qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` + $ECHO "\ + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + ECHO=\"$qECHO\" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ that is used only on +# windows platforms, and (c) all begin with the string "--lt-" +# (application programs are unlikely to have options that match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's $0 value, followed by "$@". +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=\$0 + shift + for lt_opt + do + case \"\$lt_opt\" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` + test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. + lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` + cat \"\$lt_dump_D/\$lt_dump_F\" + exit 0 + ;; + --lt-*) + \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n \"\$lt_option_debug\"; then + echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" + lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from \$@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case \" \$* \" in + *\\ --lt-*) + for lt_wr_arg + do + case \$lt_wr_arg in + --lt-*) ;; + *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; + esac + shift + done ;; + esac + func_exec_program_core \${1+\"\$@\"} +} + + # Parse options + func_parse_lt_options \"\$0\" \${1+\"\$@\"} + + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test yes = "$fast_install"; then + $ECHO "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $MKDIR \"\$progdir\" + else + $RM \"\$progdir/\$file\" + fi" + + $ECHO "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + \$ECHO \"\$relink_command_output\" >&2 + $RM \"\$progdir/\$file\" + exit 1 + fi + fi + + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $ECHO "\ + + if test -f \"\$progdir/\$program\"; then" + + # fixup the dll searchpath if we need to. + # + # Fix the DLL searchpath if we need to. Do this before prepending + # to shlibpath, because on Windows, both are PATH and uninstalled + # libraries must come first. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + # Export our shlibpath_var if we have one. + if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` + + export $shlibpath_var +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. + func_exec_program \${1+\"\$@\"} + fi + else + # The program doesn't exist. + \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 + \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 + \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" +} + + +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat < +#include +#ifdef _MSC_VER +# include +# include +# include +#else +# include +# include +# ifdef __CYGWIN__ +# include +# endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) + +/* declarations of non-ANSI functions */ +#if defined __MINGW32__ +# ifdef __STRICT_ANSI__ +int _putenv (const char *); +# endif +#elif defined __CYGWIN__ +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +/* #elif defined other_platform || defined ... */ +#endif + +/* portability defines, excluding path handling macros */ +#if defined _MSC_VER +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +# define S_IXUSR _S_IEXEC +#elif defined __MINGW32__ +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +#elif defined __CYGWIN__ +# define HAVE_SETENV +# define FOPEN_WB "wb" +/* #elif defined other platforms ... */ +#endif + +#if defined PATH_MAX +# define LT_PATHMAX PATH_MAX +#elif defined MAXPATHLEN +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +/* path handling portability macros */ +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ + defined __OS2__ +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free (stale); stale = 0; } \ +} while (0) + +#if defined LT_DEBUGWRAPPER +static int lt_debug = 1; +#else +static int lt_debug = 0; +#endif + +const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_debugprintf (const char *file, int line, const char *fmt, ...); +void lt_fatal (const char *file, int line, const char *message, ...); +static const char *nonnull (const char *s); +static const char *nonempty (const char *s); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); +char **prepare_spawn (char **argv); +void lt_dump_script (FILE *f); +EOF + + cat <= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} + +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + size_t tmp_len; + char *concat_name; + + lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", + nonempty (wrapper)); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined HAVE_DOS_BASED_FILE_SYSTEM + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined HAVE_DOS_BASED_FILE_SYSTEM + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = (size_t) (q - p); + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} + +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + lt_debugprintf (__FILE__, __LINE__, + "checking path component for symlinks: %s\n", + tmp_pathspec); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } + + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + lt_fatal (__FILE__, __LINE__, + "error accessing file \"%s\": %s", + tmp_pathspec, nonnull (strerror (errno))); + } + } + XFREE (tmp_pathspec); + + if (!has_symlinks) + { + return xstrdup (pathspec); + } + + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal (__FILE__, __LINE__, + "could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} + +char * +strendzap (char *str, const char *pat) +{ + size_t len, patlen; + + assert (str != NULL); + assert (pat != NULL); + + len = strlen (str); + patlen = strlen (pat); + + if (patlen <= len) + { + str += len - patlen; + if (STREQ (str, pat)) + *str = '\0'; + } + return str; +} + +void +lt_debugprintf (const char *file, int line, const char *fmt, ...) +{ + va_list args; + if (lt_debug) + { + (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); + } +} + +static void +lt_error_core (int exit_status, const char *file, + int line, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *file, int line, const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); + va_end (ap); +} + +static const char * +nonnull (const char *s) +{ + return s ? s : "(null)"; +} + +static const char * +nonempty (const char *s) +{ + return (s && !*s) ? "(empty)" : nonnull (s); +} + +void +lt_setenv (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_setenv) setting '%s' to '%s'\n", + nonnull (name), nonnull (value)); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + size_t len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} + +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + size_t orig_value_len = strlen (orig_value); + size_t add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} + +void +lt_update_exe_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + size_t len = strlen (new_value); + while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[--len] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +void +lt_update_lib_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +EOF + case $host_os in + mingw*) + cat <<"EOF" + +/* Prepares an argument vector before calling spawn(). + Note that spawn() does not by itself call the command interpreter + (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : + ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&v); + v.dwPlatformId == VER_PLATFORM_WIN32_NT; + }) ? "cmd.exe" : "command.com"). + Instead it simply concatenates the arguments, separated by ' ', and calls + CreateProcess(). We must quote the arguments since Win32 CreateProcess() + interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a + special way: + - Space and tab are interpreted as delimiters. They are not treated as + delimiters if they are surrounded by double quotes: "...". + - Unescaped double quotes are removed from the input. Their only effect is + that within double quotes, space and tab are treated like normal + characters. + - Backslashes not followed by double quotes are not special. + - But 2*n+1 backslashes followed by a double quote become + n backslashes followed by a double quote (n >= 0): + \" -> " + \\\" -> \" + \\\\\" -> \\" + */ +#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +char ** +prepare_spawn (char **argv) +{ + size_t argc; + char **new_argv; + size_t i; + + /* Count number of arguments. */ + for (argc = 0; argv[argc] != NULL; argc++) + ; + + /* Allocate new argument vector. */ + new_argv = XMALLOC (char *, argc + 1); + + /* Put quoted arguments into the new argument vector. */ + for (i = 0; i < argc; i++) + { + const char *string = argv[i]; + + if (string[0] == '\0') + new_argv[i] = xstrdup ("\"\""); + else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) + { + int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); + size_t length; + unsigned int backslashes; + const char *s; + char *quoted_string; + char *p; + + length = 0; + backslashes = 0; + if (quote_around) + length++; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + length += backslashes + 1; + length++; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + + quoted_string = XMALLOC (char, length + 1); + + p = quoted_string; + backslashes = 0; + if (quote_around) + *p++ = '"'; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + { + unsigned int j; + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + *p++ = c; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + unsigned int j; + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p = '\0'; + + new_argv[i] = quoted_string; + } + else + new_argv[i] = (char *) string; + } + new_argv[argc] = NULL; + + return new_argv; +} +EOF + ;; + esac + + cat <<"EOF" +void lt_dump_script (FILE* f) +{ +EOF + func_emit_wrapper yes | + $SED -n -e ' +s/^\(.\{79\}\)\(..*\)/\1\ +\2/ +h +s/\([\\"]\)/\\\1/g +s/$/\\n/ +s/\([^\n]*\).*/ fputs ("\1", f);/p +g +D' + cat <<"EOF" +} +EOF +} +# end: func_emit_cwrapperexe_src + +# func_win32_import_lib_p ARG +# True if ARG is an import lib, as indicated by $file_magic_cmd +func_win32_import_lib_p () +{ + $debug_cmd + + case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in + *import*) : ;; + *) false ;; + esac +} + +# func_suncc_cstd_abi +# !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! +# Several compiler flags select an ABI that is incompatible with the +# Cstd library. Avoid specifying it if any are in CXXFLAGS. +func_suncc_cstd_abi () +{ + $debug_cmd + + case " $compile_command " in + *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) + suncc_use_cstd_abi=no + ;; + *) + suncc_use_cstd_abi=yes + ;; + esac +} + +# func_mode_link arg... +func_mode_link () +{ + $debug_cmd + + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # what system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll that has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= + + avoid_version=no + bindir= + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + os2dllname= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=false + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module=$wl-single_module + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test yes != "$build_libtool_libs" \ + && func_fatal_configuration "cannot build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg=$1 + shift + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + func_append compile_command " @OUTPUT@" + func_append finalize_command " @OUTPUT@" + ;; + esac + + case $prev in + bindir) + bindir=$arg + prev= + continue + ;; + dlfiles|dlprefiles) + $preload || { + # Add the symbol object into the linking commands. + func_append compile_command " @SYMFILE@" + func_append finalize_command " @SYMFILE@" + preload=: + } + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test no = "$dlself"; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test dlprefiles = "$prev"; then + dlself=yes + elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test dlfiles = "$prev"; then + func_append dlfiles " $arg" + else + func_append dlprefiles " $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols=$arg + test -f "$arg" \ + || func_fatal_error "symbol file '$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex=$arg + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) func_append deplibs " $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir=$arg + prev= + continue + ;; + mllvm) + # Clang does not use LLVM to link, so we can simply discard any + # '-mllvm $arg' options when doing the link step. + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# func_append moreargs " $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test none = "$pic_object" && + test none = "$non_pic_object"; then + func_fatal_error "cannot find name of object for '$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + if test none != "$pic_object"; then + # Prepend the subdirectory the object is found in. + pic_object=$xdir$pic_object + + if test dlfiles = "$prev"; then + if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test dlprefiles = "$prev"; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg=$pic_object + fi + + # Non-PIC object. + if test none != "$non_pic_object"; then + # Prepend the subdirectory the object is found in. + non_pic_object=$xdir$non_pic_object + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test none = "$pic_object"; then + arg=$non_pic_object + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object=$pic_object + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "'$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file '$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + os2dllname) + os2dllname=$arg + prev= + continue + ;; + precious_regex) + precious_files_regex=$arg + prev= + continue + ;; + release) + release=-$arg + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test rpath = "$prev"; then + case "$rpath " in + *" $arg "*) ;; + *) func_append rpath " $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) func_append xrpath " $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds=$arg + prev= + continue + ;; + weak) + func_append weak_libs " $arg" + prev= + continue + ;; + xcclinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xcompiler) + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xlinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $wl$qarg" + prev= + func_append compile_command " $wl$qarg" + func_append finalize_command " $wl$qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg=$arg + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + # See comment for -static flag below, for more details. + func_append compile_command " $link_static_flag" + func_append finalize_command " $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "'-allow-undefined' must not be used because it is the default" + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -bindir) + prev=bindir + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test X-export-symbols = "X$arg"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework) + prev=framework + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + func_append compile_command " $arg" + func_append finalize_command " $arg" + ;; + esac + continue + ;; + + -L*) + func_stripname "-L" '' "$arg" + if test -z "$func_stripname_result"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between '-L' and '$1'" + else + func_fatal_error "need path for '-L' option" + fi + fi + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of '$dir'" + dir=$absdir + ;; + esac + case "$deplibs " in + *" -L$dir "* | *" $arg "*) + # Will only happen for absolute or sysroot arguments + ;; + *) + # Preserve sysroot, but never include relative directories + case $dir in + [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; + *) func_append deplibs " -L$dir" ;; + esac + func_append lib_search_path " $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) func_append dllsearchpath ":$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test X-lc = "X$arg" || test X-lm = "X$arg"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test X-lc = "X$arg" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) + # Do not include libc due to us having libc/libc_r. + test X-lc = "X$arg" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + func_append deplibs " System.ltframework" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test X-lc = "X$arg" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test X-lc = "X$arg" && continue + ;; + esac + elif test X-lc_r = "X$arg"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + func_append deplibs " $arg" + continue + ;; + + -mllvm) + prev=mllvm + continue + ;; + + -module) + module=yes + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + # Darwin uses the -arch flag to determine output architecture. + -model|-arch|-isysroot|--sysroot) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) func_append new_inherited_linker_flags " $arg" ;; + esac + continue + ;; + + -multi_module) + single_module=$wl-multi_module + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "'-no-install' is ignored for $host" + func_warning "assuming '-no-fast-install' instead" + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -os2dllname) + prev=os2dllname + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + =*) + func_stripname '=' '' "$dir" + dir=$lt_sysroot$func_stripname_result + ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + continue + ;; + + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -static | -static-libtool-libs) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -weak) + prev=weak + continue + ;; + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs=$IFS; IFS=, + for flag in $args; do + IFS=$save_ifs + func_quote_for_eval "$flag" + func_append arg " $func_quote_for_eval_result" + func_append compiler_flags " $func_quote_for_eval_result" + done + IFS=$save_ifs + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs=$IFS; IFS=, + for flag in $args; do + IFS=$save_ifs + func_quote_for_eval "$flag" + func_append arg " $wl$func_quote_for_eval_result" + func_append compiler_flags " $wl$func_quote_for_eval_result" + func_append linker_flags " $func_quote_for_eval_result" + done + IFS=$save_ifs + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + ;; + + # Flags to be passed through unchanged, with rationale: + # -64, -mips[0-9] enable 64-bit mode for the SGI compiler + # -r[0-9][0-9]* specify processor for the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler + # +DA*, +DD* enable 64-bit mode for the HP compiler + # -q* compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* architecture-specific flags for GCC + # -F/path path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* profiling flags for GCC + # -fstack-protector* stack protector flags for GCC + # @file GCC response files + # -tp=* Portland pgcc target processor selection + # --sysroot=* for sysroot support + # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + # -specs=* GCC specs files + # -stdlib=* select c++ std lib with clang + # -fsanitize=* Clang/GCC memory and address sanitizer + # -fuse-ld=* Linker select flags for GCC + # -static-* direct GCC to link specific libraries statically + # -fcilkplus Cilk Plus language extension features for C/C++ + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ + -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ + -specs=*|-fsanitize=*|-fuse-ld=*|-static-*|-fcilkplus) + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + func_append compile_command " $arg" + func_append finalize_command " $arg" + func_append compiler_flags " $arg" + continue + ;; + + -Z*) + if test os2 = "`expr $host : '.*\(os2\)'`"; then + # OS/2 uses -Zxxx to specify OS/2-specific options + compiler_flags="$compiler_flags $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case $arg in + -Zlinker | -Zstack) + prev=xcompiler + ;; + esac + continue + else + # Otherwise treat like 'Some other compiler flag' below + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + fi + ;; + + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + ;; + + *.$objext) + # A standard object. + func_append objs " $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test none = "$pic_object" && + test none = "$non_pic_object"; then + func_fatal_error "cannot find name of object for '$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + test none = "$pic_object" || { + # Prepend the subdirectory the object is found in. + pic_object=$xdir$pic_object + + if test dlfiles = "$prev"; then + if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test dlprefiles = "$prev"; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg=$pic_object + } + + # Non-PIC object. + if test none != "$non_pic_object"; then + # Prepend the subdirectory the object is found in. + non_pic_object=$xdir$non_pic_object + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test none = "$pic_object"; then + arg=$non_pic_object + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object=$pic_object + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "'$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + func_append deplibs " $arg" + func_append old_deplibs " $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + func_resolve_sysroot "$arg" + if test dlfiles = "$prev"; then + # This library was specified with -dlopen. + func_append dlfiles " $func_resolve_sysroot_result" + prev= + elif test dlprefiles = "$prev"; then + # The library was specified with -dlpreopen. + func_append dlprefiles " $func_resolve_sysroot_result" + prev= + else + func_append deplibs " $func_resolve_sysroot_result" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the '$prevarg' option requires an argument" + + if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname=$func_basename_result + libobjs_save=$libobjs + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + # Definition is injected by LT_CONFIG during libtool generation. + func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" + + func_dirname "$output" "/" "" + output_objdir=$func_dirname_result$objdir + func_to_tool_file "$output_objdir/" + tool_output_objdir=$func_to_tool_file_result + # Create the object directory. + func_mkdir_p "$output_objdir" + + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if $opt_preserve_dup_deps; then + case "$libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append libs " $deplib" + done + + if test lib = "$linkmode"; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; + esac + func_append pre_post_deps " $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + + case $linkmode in + lib) + passes="conv dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=false + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + + for pass in $passes; do + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test lib,link = "$linkmode,$pass"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs=$tmp_deplibs + fi + + if test lib,link = "$linkmode,$pass" || + test prog,scan = "$linkmode,$pass"; then + libs=$deplibs + deplibs= + fi + if test prog = "$linkmode"; then + case $pass in + dlopen) libs=$dlfiles ;; + dlpreopen) libs=$dlprefiles ;; + link) + libs="$deplibs %DEPLIBS%" + test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" + ;; + esac + fi + if test lib,dlpreopen = "$linkmode,$pass"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + func_resolve_sysroot "$lib" + case $lib in + *.la) func_source "$func_resolve_sysroot_result" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + func_basename "$deplib" + deplib_base=$func_basename_result + case " $weak_libs " in + *" $deplib_base "*) ;; + *) func_append deplibs " $deplib" ;; + esac + done + done + libs=$dlprefiles + fi + if test dlopen = "$pass"; then + # Collect dlpreopened libraries + save_deplibs=$deplibs + deplibs= + fi + + for deplib in $libs; do + lib= + found=false + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append compiler_flags " $deplib" + if test lib = "$linkmode"; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test lib != "$linkmode" && test prog != "$linkmode"; then + func_warning "'-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test lib = "$linkmode"; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib=$searchdir/lib$name$search_ext + if test -f "$lib"; then + if test .la = "$search_ext"; then + found=: + else + found=false + fi + break 2 + fi + done + done + if $found; then + # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + case " $predeps $postdeps " in + *" $deplib "*) + if func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + for l in $old_library $library_names; do + ll=$l + done + if test "X$ll" = "X$old_library"; then # only static version available + found=false + func_dirname "$lib" "" "." + ladir=$func_dirname_result + lib=$ladir/$old_library + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + else + # deplib doesn't seem to be a libtool library + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + ;; # -l + *.ltframework) + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test lib = "$linkmode"; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test conv = "$pass" && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + prog) + if test conv = "$pass"; then + deplibs="$deplib $deplibs" + continue + fi + if test scan = "$pass"; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + *) + func_warning "'-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test link = "$pass"; then + func_stripname '-R' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) + func_resolve_sysroot "$deplib" + lib=$func_resolve_sysroot_result + ;; + *.$libext) + if test conv = "$pass"; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=false + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=: + fi + ;; + pass_all) + valid_a_lib=: + ;; + esac + if $valid_a_lib; then + echo + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + else + echo + $ECHO "*** Warning: Trying to link with static lib archive $deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because the file extensions .$libext of this argument makes me believe" + echo "*** that it is just a static archive that I should not use here." + fi + ;; + esac + continue + ;; + prog) + if test link != "$pass"; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test conv = "$pass"; then + deplibs="$deplib $deplibs" + elif test prog = "$linkmode"; then + if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + func_append newdlprefiles " $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append newdlfiles " $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=: + continue + ;; + esac # case $deplib + + $found || test -f "$lib" \ + || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "'$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir=$func_dirname_result + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + if test lib,link = "$linkmode,$pass" || + test prog,scan = "$linkmode,$pass" || + { test prog != "$linkmode" && test lib != "$linkmode"; }; then + test -n "$dlopen" && func_append dlfiles " $dlopen" + test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" + fi + + if test conv = "$pass"; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + func_fatal_error "cannot find name of link library for '$lib'" + fi + # It is a libtool convenience library, so add in its objects. + func_append convenience " $ladir/$objdir/$old_library" + func_append old_convenience " $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_preserve_dup_deps; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done + elif test prog != "$linkmode" && test lib != "$linkmode"; then + func_fatal_error "'$lib' is not a convenience library" + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + if test -n "$old_library" && + { test yes = "$prefer_static_libs" || + test built,no = "$prefer_static_libs,$installed"; }; then + linklib=$old_library + else + for l in $old_library $library_names; do + linklib=$l + done + fi + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for '$lib'" + fi + + # This library was specified with -dlopen. + if test dlopen = "$pass"; then + test -z "$libdir" \ + && func_fatal_error "cannot -dlopen a convenience library: '$lib'" + if test -z "$dlname" || + test yes != "$dlopen_support" || + test no = "$build_libtool_libs" + then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + func_append dlprefiles " $lib $dependency_libs" + else + func_append newdlfiles " $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + func_warning "cannot determine absolute directory name of '$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir=$ladir + fi + ;; + esac + func_basename "$lib" + laname=$func_basename_result + + # Find the relevant object directory and library name. + if test yes = "$installed"; then + if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library '$lib' was moved." + dir=$ladir + absdir=$abs_ladir + libdir=$abs_ladir + else + dir=$lt_sysroot$libdir + absdir=$lt_sysroot$libdir + fi + test yes = "$hardcode_automatic" && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir=$ladir + absdir=$abs_ladir + # Remove this search path later + func_append notinst_path " $abs_ladir" + else + dir=$ladir/$objdir + absdir=$abs_ladir/$objdir + # Remove this search path later + func_append notinst_path " $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test dlpreopen = "$pass"; then + if test -z "$libdir" && test prog = "$linkmode"; then + func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" + fi + case $host in + # special handling for platforms with PE-DLLs. + *cygwin* | *mingw* | *cegcc* ) + # Linker will automatically link against shared library if both + # static and shared are present. Therefore, ensure we extract + # symbols from the import library if a shared library is present + # (otherwise, the dlopen module name will be incorrect). We do + # this by putting the import library name into $newdlprefiles. + # We recover the dlopen module name by 'saving' the la file + # name in a special purpose variable, and (later) extracting the + # dlname from the la file. + if test -n "$dlname"; then + func_tr_sh "$dir/$linklib" + eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" + func_append newdlprefiles " $dir/$linklib" + else + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + fi + ;; + * ) + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + func_append newdlprefiles " $dir/$dlname" + else + func_append newdlprefiles " $dir/$linklib" + fi + ;; + esac + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test lib = "$linkmode"; then + deplibs="$dir/$old_library $deplibs" + elif test prog,link = "$linkmode,$pass"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test prog = "$linkmode" && test link != "$pass"; then + func_append newlib_search_path " $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=false + if test no != "$link_all_deplibs" || test -z "$library_names" || + test no = "$build_libtool_libs"; then + linkalldeplibs=: + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + esac + # Need to link against all dependency_libs? + if $linkalldeplibs; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if $opt_preserve_dup_deps; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test prog,link = "$linkmode,$pass"; then + if test -n "$library_names" && + { { test no = "$prefer_static_libs" || + test built,yes = "$prefer_static_libs,$installed"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then + # Make sure the rpath contains only unique directories. + case $temp_rpath: in + *"$absdir:"*) ;; + *) func_append temp_rpath "$absdir:" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if $alldeplibs && + { test pass_all = "$deplibs_check_method" || + { test yes = "$build_libtool_libs" && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test built = "$use_static_libs" && test yes = "$installed"; then + use_static_libs=no + fi + if test -n "$library_names" && + { test no = "$use_static_libs" || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc* | *os2*) + # No point in relinking DLLs because paths are not encoded + func_append notinst_deplibs " $lib" + need_relink=no + ;; + *) + if test no = "$installed"; then + func_append notinst_deplibs " $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule= + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule=$dlpremoduletest + break + fi + done + if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then + echo + if test prog = "$linkmode"; then + $ECHO "*** Warning: Linking the executable $output against the loadable module" + else + $ECHO "*** Warning: Linking the shared library $output against the loadable module" + fi + $ECHO "*** $linklib is not portable!" + fi + if test lib = "$linkmode" && + test yes = "$hardcode_into_libs"; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname=$1 + shift + libname=`eval "\\$ECHO \"$libname_spec\""` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname=$dlname + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw* | *cegcc* | *os2*) + func_arith $current - $age + major=$func_arith_result + versuffix=-$major + ;; + esac + eval soname=\"$soname_spec\" + else + soname=$realname + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot=$soname + func_basename "$soroot" + soname=$func_basename_result + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from '$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for '$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test prog = "$linkmode" || test relink != "$opt_mode"; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test no = "$hardcode_direct"; then + add=$dir/$linklib + case $host in + *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; + *-*-sysv4*uw2*) add_dir=-L$dir ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir=-L$dir ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we cannot + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null; then + if test "X$dlopenmodule" != "X$lib"; then + $ECHO "*** Warning: lib $linklib is a module, not a shared library" + if test -z "$old_library"; then + echo + echo "*** And there doesn't seem to be a static archive available" + echo "*** The link will probably fail, sorry" + else + add=$dir/$old_library + fi + elif test -n "$old_library"; then + add=$dir/$old_library + fi + fi + esac + elif test no = "$hardcode_minus_L"; then + case $host in + *-*-sunos*) add_shlibpath=$dir ;; + esac + add_dir=-L$dir + add=-l$name + elif test no = "$hardcode_shlibpath_var"; then + add_shlibpath=$dir + add=-l$name + else + lib_linked=no + fi + ;; + relink) + if test yes = "$hardcode_direct" && + test no = "$hardcode_direct_absolute"; then + add=$dir/$linklib + elif test yes = "$hardcode_minus_L"; then + add_dir=-L$absdir + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add=-l$name + elif test yes = "$hardcode_shlibpath_var"; then + add_shlibpath=$dir + add=-l$name + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test yes != "$lib_linked"; then + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) func_append compile_shlibpath "$add_shlibpath:" ;; + esac + fi + if test prog = "$linkmode"; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test yes != "$hardcode_direct" && + test yes != "$hardcode_minus_L" && + test yes = "$hardcode_shlibpath_var"; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + fi + fi + fi + + if test prog = "$linkmode" || test relink = "$opt_mode"; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test yes = "$hardcode_direct" && + test no = "$hardcode_direct_absolute"; then + add=$libdir/$linklib + elif test yes = "$hardcode_minus_L"; then + add_dir=-L$libdir + add=-l$name + elif test yes = "$hardcode_shlibpath_var"; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + add=-l$name + elif test yes = "$hardcode_automatic"; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib"; then + add=$inst_prefix_dir$libdir/$linklib + else + add=$libdir/$linklib + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir=-L$libdir + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add=-l$name + fi + + if test prog = "$linkmode"; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test prog = "$linkmode"; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test unsupported != "$hardcode_direct"; then + test -n "$old_library" && linklib=$old_library + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test yes = "$build_libtool_libs"; then + # Not a shared library + if test pass_all != "$deplibs_check_method"; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + echo + $ECHO "*** Warning: This system cannot link to static lib archive $lib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have." + if test yes = "$module"; then + echo "*** But as you try to build a module library, libtool will still create " + echo "*** a static module, that should work as long as the dlopening application" + echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using 'nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** 'nm' from GNU binutils and a full rebuild may help." + fi + if test no = "$build_old_libs"; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test lib = "$linkmode"; then + if test -n "$dependency_libs" && + { test yes != "$hardcode_into_libs" || + test yes = "$build_old_libs" || + test yes = "$link_static"; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) func_append xrpath " $temp_xrpath";; + esac;; + *) func_append temp_deplibs " $libdir";; + esac + done + dependency_libs=$temp_deplibs + fi + + func_append newlib_search_path " $absdir" + # Link against this library + test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result";; + *) func_resolve_sysroot "$deplib" ;; + esac + if $opt_preserve_dup_deps; then + case "$tmp_libs " in + *" $func_resolve_sysroot_result "*) + func_append specialdeplibs " $func_resolve_sysroot_result" ;; + esac + fi + func_append tmp_libs " $func_resolve_sysroot_result" + done + + if test no != "$link_all_deplibs"; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + path= + case $deplib in + -L*) path=$deplib ;; + *.la) + func_resolve_sysroot "$deplib" + deplib=$func_resolve_sysroot_result + func_dirname "$deplib" "" "." + dir=$func_dirname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of '$dir'" + absdir=$dir + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names"; then + for tmp in $deplibrary_names; do + depdepl=$tmp + done + if test -f "$absdir/$objdir/$depdepl"; then + depdepl=$absdir/$objdir/$depdepl + darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" + func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" + path= + fi + fi + ;; + *) + path=-L$absdir/$objdir + ;; + esac + else + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "'$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "'$deplib' seems to be moved" + + path=-L$absdir + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test link = "$pass"; then + if test prog = "$linkmode"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + dependency_libs=$newdependency_libs + if test dlpreopen = "$pass"; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test dlopen != "$pass"; then + test conv = "$pass" || { + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) func_append lib_search_path " $dir" ;; + esac + done + newlib_search_path= + } + + if test prog,link = "$linkmode,$pass"; then + vars="compile_deplibs finalize_deplibs" + else + vars=deplibs + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) func_append tmp_libs " $deplib" ;; + esac + ;; + *) func_append tmp_libs " $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + + # Add Sun CC postdeps if required: + test CXX = "$tagname" && { + case $host_os in + linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C++ 5.9 + func_suncc_cstd_abi + + if test no != "$suncc_use_cstd_abi"; then + func_append postdeps ' -library=Cstd -library=Crun' + fi + ;; + esac + ;; + + solaris*) + func_cc_basename "$CC" + case $func_cc_basename_result in + CC* | sunCC*) + func_suncc_cstd_abi + + if test no != "$suncc_use_cstd_abi"; then + func_append postdeps ' -library=Cstd -library=Crun' + fi + ;; + esac + ;; + esac + } + + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i= + ;; + esac + if test -n "$i"; then + func_append tmp_libs " $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test prog = "$linkmode"; then + dlfiles=$newdlfiles + fi + if test prog = "$linkmode" || test lib = "$linkmode"; then + dlprefiles=$newdlprefiles + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then + func_warning "'-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "'-l' and '-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "'-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "'-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "'-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "'-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "'-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs=$output + func_append objs "$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form 'libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test no = "$module" \ + && func_fatal_help "libtool library '$output' must begin with 'lib'" + + if test no != "$need_lib_prefix"; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test pass_all != "$deplibs_check_method"; then + func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" + else + echo + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + func_append libobjs " $objs" + fi + fi + + test no = "$dlself" \ + || func_warning "'-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test 1 -lt "$#" \ + && func_warning "ignoring multiple '-rpath's for a libtool library" + + install_libdir=$1 + + oldlibs= + if test -z "$rpath"; then + if test yes = "$build_libtool_libs"; then + # Building a libtool convenience library. + # Some compilers have problems with a '.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + test -n "$vinfo" && \ + func_warning "'-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "'-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs=$IFS; IFS=: + set dummy $vinfo 0 0 0 + shift + IFS=$save_ifs + + test -n "$7" && \ + func_fatal_help "too many parameters to '-version-info'" + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major=$1 + number_minor=$2 + number_revision=$3 + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # that has an extra 1 added just for fun + # + case $version_type in + # correct linux to gnu/linux during the next big refactor + darwin|freebsd-elf|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age=$number_minor + revision=$number_revision + ;; + freebsd-aout|qnx|sunos) + current=$number_major + revision=$number_minor + age=0 + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age=$number_minor + revision=$number_minor + lt_irix_increment=no + ;; + *) + func_fatal_configuration "$modename: unknown library version type '$version_type'" + ;; + esac + ;; + no) + current=$1 + revision=$2 + age=$3 + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT '$current' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION '$revision' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE '$age' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE '$age' is greater than the current interface number '$current'" + func_fatal_error "'$vinfo' is not valid version information" + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + func_arith $current - $age + major=.$func_arith_result + versuffix=$major.$age.$revision + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + # On Darwin other compilers + case $CC in + nagfor*) + verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" + ;; + *) + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + esac + ;; + + freebsd-aout) + major=.$current + versuffix=.$current.$revision + ;; + + freebsd-elf) + func_arith $current - $age + major=.$func_arith_result + versuffix=$major.$age.$revision + ;; + + irix | nonstopux) + if test no = "$lt_irix_increment"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring=$verstring_prefix$major.$revision + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test 0 -ne "$loop"; do + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring=$verstring_prefix$major.$iface:$verstring + done + + # Before this point, $major must not contain '.'. + major=.$major + versuffix=$major.$revision + ;; + + linux) # correct to gnu/linux during the next big refactor + func_arith $current - $age + major=.$func_arith_result + versuffix=$major.$age.$revision + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + versuffix=.$current.$age.$revision + verstring=$current.$age.$revision + + # Add in all the interfaces that we are compatible with. + loop=$age + while test 0 -ne "$loop"; do + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring=$verstring:$iface.0 + done + + # Make executables depend on our current version. + func_append verstring ":$current.0" + ;; + + qnx) + major=.$current + versuffix=.$current + ;; + + sco) + major=.$current + versuffix=.$current + ;; + + sunos) + major=.$current + versuffix=.$current.$revision + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 file systems. + func_arith $current - $age + major=$func_arith_result + versuffix=-$major + ;; + + *) + func_fatal_configuration "unknown library version type '$version_type'" + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring=0.0 + ;; + esac + if test no = "$need_version"; then + versuffix= + else + versuffix=.0.0 + fi + fi + + # Remove version info from name if versioning should be avoided + if test yes,no = "$avoid_version,$need_version"; then + major= + versuffix= + verstring= + fi + + # Check to see if the archive will have undefined symbols. + if test yes = "$allow_undefined"; then + if test unsupported = "$allow_undefined_flag"; then + if test yes = "$build_old_libs"; then + func_warning "undefined symbols not allowed in $host shared libraries; building static only" + build_libtool_libs=no + else + func_fatal_error "can't build $host shared library unless -no-undefined is specified" + fi + fi + else + # Don't allow undefined symbols. + allow_undefined_flag=$no_undefined_flag + fi + + fi + + func_generate_dlsyms "$libname" "$libname" : + func_append libobjs " $symfileobj" + test " " = "$libobjs" && libobjs= + + if test relink != "$opt_mode"; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$ECHO "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext | *.gcno) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) + if test -n "$precious_files_regex"; then + if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + func_append removelist " $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then + func_append oldlibs " $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` + # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` + # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` + #done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + func_replace_sysroot "$libdir" + func_append temp_xrpath " -R$func_replace_sysroot_result" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles=$dlfiles + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) func_append dlfiles " $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles=$dlprefiles + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) func_append dlprefiles " $lib" ;; + esac + done + + if test yes = "$build_libtool_libs"; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + func_append deplibs " System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test yes = "$build_libtool_need_lc"; then + func_append deplibs " -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release= + versuffix= + major= + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $opt_dry_run || $RM conftest.c + cat > conftest.c </dev/null` + $nocaseglob + else + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + fi + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null | + $GREP " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib=$potent_lib + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | $SED 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; + *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib= + break 2 + fi + done + done + fi + if test -n "$a_deplib"; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib"; then + $ECHO "*** with $libname but no candidates were found. (...for file magic test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a file magic. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + case " $predeps $postdeps " in + *" $a_deplib "*) + func_append newdeplibs " $a_deplib" + a_deplib= + ;; + esac + fi + if test -n "$a_deplib"; then + libname=`eval "\\$ECHO \"$libname_spec\""` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib=$potent_lib # see symlink-check above in file_magic test + if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ + $EGREP "$match_pattern_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib= + break 2 + fi + done + done + fi + if test -n "$a_deplib"; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib"; then + $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a regex pattern. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs= + tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + for i in $predeps $postdeps; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` + done + fi + case $tmp_deplibs in + *[!\ \ ]*) + echo + if test none = "$deplibs_check_method"; then + echo "*** Warning: inter-library dependencies are not supported in this platform." + else + echo "*** Warning: inter-library dependencies are not known to be supported." + fi + echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + ;; + esac + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library with the System framework + newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + if test yes = "$droppeddeps"; then + if test yes = "$module"; then + echo + echo "*** Warning: libtool could not satisfy all declared inter-library" + $ECHO "*** dependencies of module $libname. Therefore, libtool will create" + echo "*** a static module, that should work as long as the dlopening" + echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using 'nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** 'nm' from GNU binutils and a full rebuild may help." + fi + if test no = "$build_old_libs"; then + oldlibs=$output_objdir/$libname.$libext + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + echo "*** The inter-library dependencies that have been dropped here will be" + echo "*** automatically added whenever a program is linked with this library" + echo "*** or is declared to -dlopen it." + + if test no = "$allow_undefined"; then + echo + echo "*** Since this library must not contain undefined symbols," + echo "*** because either the platform does not support them or" + echo "*** it was explicitly requested with -no-undefined," + echo "*** libtool will only create a static version of it." + if test no = "$build_old_libs"; then + oldlibs=$output_objdir/$libname.$libext + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + deplibs=$new_libs + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test yes = "$build_libtool_libs"; then + # Remove $wl instances when linking with ld. + # FIXME: should test the right _cmds variable. + case $archive_cmds in + *\$LD\ *) wl= ;; + esac + if test yes = "$hardcode_into_libs"; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath=$finalize_rpath + test relink = "$opt_mode" || rpath=$compile_rpath$rpath + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + func_replace_sysroot "$libdir" + libdir=$func_replace_sysroot_result + if test -z "$hardcode_libdirs"; then + hardcode_libdirs=$libdir + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append dep_rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir=$hardcode_libdirs + eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath=$finalize_shlibpath + test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + shift + realname=$1 + shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname=$realname + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib=$output_objdir/$realname + linknames= + for link + do + func_append linknames " $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= + + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols=$output_objdir/$libname.uexp + func_append delfiles " $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + func_dll_def_p "$export_symbols" || { + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols=$export_symbols + export_symbols= + always_export_symbols=yes + } + fi + ;; + esac + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for '$libname.la'" + export_symbols=$output_objdir/$libname.exp + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs=$IFS; IFS='~' + for cmd1 in $cmds; do + IFS=$save_ifs + # Take the normal branch if the nm_file_list_spec branch + # doesn't work or if tool conversion is not needed. + case $nm_file_list_spec~$to_tool_file_cmd in + *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) + try_normal_branch=yes + eval cmd=\"$cmd1\" + func_len " $cmd" + len=$func_len_result + ;; + *) + try_normal_branch=no + ;; + esac + if test yes = "$try_normal_branch" \ + && { test "$len" -lt "$max_cmd_len" \ + || test "$max_cmd_len" -le -1; } + then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + elif test -n "$nm_file_list_spec"; then + func_basename "$output" + output_la=$func_basename_result + save_libobjs=$libobjs + save_output=$output + output=$output_objdir/$output_la.nm + func_to_tool_file "$output" + libobjs=$nm_file_list_spec$func_to_tool_file_result + func_append delfiles " $output" + func_verbose "creating $NM input file list: $output" + for obj in $save_libobjs; do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > "$output" + eval cmd=\"$cmd1\" + func_show_eval "$cmd" 'exit $?' + output=$save_output + libobjs=$save_libobjs + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS=$save_ifs + if test -n "$export_symbols_regex" && test : != "$skipped_export"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols=$export_symbols + test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test : != "$skipped_export" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for '$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands, which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + func_append tmp_deplibs " $test_deplib" + ;; + esac + done + deplibs=$tmp_deplibs + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test yes = "$compiler_needs_object" && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi + + if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + func_append linker_flags " $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test relink = "$opt_mode"; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test yes = "$module" && test -n "$module_cmds"; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test : != "$skipped_export" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + func_basename "$output" + output_la=$func_basename_result + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 + + if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then + output=$output_objdir/$output_la.lnkscript + func_verbose "creating GNU ld script: $output" + echo 'INPUT (' > $output + for obj in $save_libobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + echo ')' >> $output + func_append delfiles " $output" + func_to_tool_file "$output" + output=$func_to_tool_file_result + elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then + output=$output_objdir/$output_la.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test yes = "$compiler_needs_object"; then + firstobj="$1 " + shift + fi + for obj + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + func_append delfiles " $output" + func_to_tool_file "$output" + output=$firstobj\"$file_list_spec$func_to_tool_file_result\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-$k.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test -z "$objlist" || + test "$len" -lt "$max_cmd_len"; then + func_append objlist " $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test 1 -eq "$k"; then + # The first file doesn't have a previous command to add. + reload_objs=$objlist + eval concat_cmds=\"$reload_cmds\" + else + # All subsequent reloadable object files will link in + # the last one created. + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-$k.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-$k.$objext + objlist=" $obj" + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds$reload_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + func_append delfiles " $output" + + else + output= + fi + + ${skipped_export-false} && { + func_verbose "generating symbol list for '$libname.la'" + export_symbols=$output_objdir/$libname.exp + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + } + + test -n "$save_libobjs" && + func_verbose "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs=$IFS; IFS='~' + for cmd in $concat_cmds; do + IFS=$save_ifs + $opt_quiet || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test relink = "$opt_mode"; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS=$save_ifs + + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + + ${skipped_export-false} && { + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols=$export_symbols + test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for '$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands, which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + } + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test yes = "$module" && test -n "$module_cmds"; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + fi + + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + + save_ifs=$IFS; IFS='~' + for cmd in $cmds; do + IFS=$sp$nl + eval cmd=\"$cmd\" + IFS=$save_ifs + $opt_quiet || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test relink = "$opt_mode"; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS=$save_ifs + + # Restore the uninstalled library and exit + if test relink = "$opt_mode"; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test yes = "$module" || test yes = "$export_dynamic"; then + # On all known operating systems, these are identical. + dlname=$soname + fi + fi + ;; + + obj) + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then + func_warning "'-dlopen' is ignored for objects" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "'-l' and '-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "'-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "'-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "'-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "'-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object '$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result + ;; + *) + libobj= + obj=$output + ;; + esac + + # Delete the old objects. + $opt_dry_run || $RM $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # if reload_cmds runs $LD directly, get rid of -Wl from + # whole_archive_flag_spec and hope we can get by with turning comma + # into space. + case $reload_cmds in + *\$LD[\ \$]*) wl= ;; + esac + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` + reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags + else + gentop=$output_objdir/${obj}x + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # If we're not building shared, we need to use non_pic_objs + test yes = "$build_libtool_libs" || libobjs=$non_pic_objects + + # Create the old-style object. + reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs + + output=$obj + func_execute_cmds "$reload_cmds" 'exit $?' + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + fi + + test yes = "$build_libtool_libs" || { + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + } + + if test -n "$pic_flag" || test default != "$pic_mode"; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output=$libobj + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "'-version-info' is ignored for programs" + + test -n "$release" && \ + func_warning "'-release' is ignored for programs" + + $preload \ + && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ + && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test CXX = "$tagname"; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + func_append compile_command " $wl-bind_at_load" + func_append finalize_command " $wl-bind_at_load" + ;; + esac + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + compile_deplibs=$new_libs + + + func_append compile_command " $compile_deplibs" + func_append finalize_command " $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs=$libdir + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) func_append dllsearchpath ":$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir=$hardcode_libdirs + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath=$rpath + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs=$libdir + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) func_append finalize_perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir=$hardcode_libdirs + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath=$rpath + + if test -n "$libobjs" && test yes = "$build_old_libs"; then + # Transform all the library objects into standard objects. + compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + fi + + func_generate_dlsyms "$outputname" "@PROGRAM@" false + + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi + + wrappers_required=: + case $host in + *cegcc* | *mingw32ce*) + # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. + wrappers_required=false + ;; + *cygwin* | *mingw* ) + test yes = "$build_libtool_libs" || wrappers_required=false + ;; + *) + if test no = "$need_relink" || test yes != "$build_libtool_libs"; then + wrappers_required=false + fi + ;; + esac + $wrappers_required || { + # Replace the output file specification. + compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + link_command=$compile_command$compile_rpath + + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.$objext"; then + func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' + fi + + exit $exit_status + } + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + func_append rpath "$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test yes = "$no_install"; then + # We don't need to create a wrapper script. + link_command=$compile_var$compile_command$compile_rpath + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + exit $EXIT_SUCCESS + fi + + case $hardcode_action,$fast_install in + relink,*) + # Fast installation is not supported + link_command=$compile_var$compile_command$compile_rpath + relink_command=$finalize_var$finalize_command$finalize_rpath + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "'$output' will be relinked during installation" + ;; + *,yes) + link_command=$finalize_var$compile_command$finalize_rpath + relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` + ;; + *,no) + link_command=$compile_var$compile_command$compile_rpath + relink_command=$finalize_var$finalize_command$finalize_rpath + ;; + *,needless) + link_command=$finalize_var$compile_command$finalize_rpath + relink_command= + ;; + esac + + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname + + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output_objdir/$outputname" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Now create the wrapper script. + func_verbose "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource=$output_path/$objdir/lt-$output_name.c + cwrapper=$output_path/$output_name.exe + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } + + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host"; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 + + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + case $build_libtool_libs in + convenience) + oldobjs="$libobjs_save $symfileobj" + addlibs=$convenience + build_libtool_libs=no + ;; + module) + oldobjs=$libobjs_save + addlibs=$old_convenience + build_libtool_libs=no + ;; + *) + oldobjs="$old_deplibs $non_pic_objects" + $preload && test -f "$symfileobj" \ + && func_append oldobjs " $symfileobj" + addlibs=$old_convenience + ;; + esac + + if test -n "$addlibs"; then + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $addlibs + func_append oldobjs " $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then + cmds=$old_archive_from_new_cmds + else + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append oldobjs " $func_extract_archives_result" + fi + + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + echo "copying selected object files to avoid basename conflicts..." + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase=$func_basename_result + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + func_append oldobjs " $gentop/$newobj" + ;; + *) func_append oldobjs " $obj" ;; + esac + done + fi + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + elif test -n "$archiver_list_spec"; then + func_verbose "using command file archive linking..." + for obj in $oldobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > $output_objdir/$libname.libcmd + func_to_tool_file "$output_objdir/$libname.libcmd" + oldobjs=" $archiver_list_spec$func_to_tool_file_result" + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + func_append objlist " $obj" + if test "$len" -lt "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj"; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" + objlist= + len=$len0 + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test -z "$oldobjs"; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + func_execute_cmds "$cmds" 'exit $?' + done + + test -n "$generated" && \ + func_show_eval "${RM}r$generated" + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test yes = "$build_old_libs" && old_library=$libname.$libext + func_verbose "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + if test yes = "$hardcode_automatic"; then + relink_command= + fi + + # Only create the output if not a dry run. + $opt_dry_run || { + for installed in no yes; do + if test yes = "$installed"; then + if test -z "$install_libdir"; then + break + fi + output=$output_objdir/${outputname}i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + func_basename "$deplib" + name=$func_basename_result + func_resolve_sysroot "$deplib" + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` + test -z "$libdir" && \ + func_fatal_error "'$deplib' is not a valid libtool archive" + func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" + ;; + -L*) + func_stripname -L '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -L$func_replace_sysroot_result" + ;; + -R*) + func_stripname -R '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -R$func_replace_sysroot_result" + ;; + *) func_append newdependency_libs " $deplib" ;; + esac + done + dependency_libs=$newdependency_libs + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name=$func_basename_result + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "'$lib' is not a valid libtool archive" + func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" + ;; + *) func_append newdlfiles " $lib" ;; + esac + done + dlfiles=$newdlfiles + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name=$func_basename_result + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "'$lib' is not a valid libtool archive" + func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" + ;; + esac + done + dlprefiles=$newdlprefiles + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlfiles " $abs" + done + dlfiles=$newdlfiles + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlprefiles " $abs" + done + dlprefiles=$newdlprefiles + fi + $RM $output + # place dlname in correct position for cygwin + # In fact, it would be nice if we could use this code for all target + # systems that can't hard-code library paths into their executables + # and that have no shared library path variable independent of PATH, + # but it turns out we can't easily determine that from inspecting + # libtool variables, so we have to hard-code the OSs to which it + # applies here; at the moment, that means platforms that use the PE + # object format with DLL files. See the long comment at the top of + # tests/bindir.at for full details. + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) + # If a -bindir argument was supplied, place the dll there. + if test -n "$bindir"; then + func_relative_path "$install_libdir" "$bindir" + tdlname=$func_relative_path_result/$dlname + else + # Otherwise fall back on heuristic. + tdlname=../bin/$dlname + fi + ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Names of additional weak libraries provided by this library +weak_library_names='$weak_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test no,yes = "$installed,$need_relink"; then + $ECHO >> $output "\ +relink_command=\"$relink_command\"" + fi + done + } + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} + +if test link = "$opt_mode" || test relink = "$opt_mode"; then + func_mode_link ${1+"$@"} +fi + + +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $debug_cmd + + RM=$nonopt + files= + rmforce=false + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic=$magic + + for arg + do + case $arg in + -f) func_append RM " $arg"; rmforce=: ;; + -*) func_append RM " $arg" ;; + *) func_append files " $arg" ;; + esac + done + + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" + + rmdirs= + + for file in $files; do + func_dirname "$file" "" "." + dir=$func_dirname_result + if test . = "$dir"; then + odir=$objdir + else + odir=$dir/$objdir + fi + func_basename "$file" + name=$func_basename_result + test uninstall = "$opt_mode" && odir=$dir + + # Remember odir for removal later, being careful to avoid duplicates + if test clean = "$opt_mode"; then + case " $rmdirs " in + *" $odir "*) ;; + *) func_append rmdirs " $odir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if { test -L "$file"; } >/dev/null 2>&1 || + { test -h "$file"; } >/dev/null 2>&1 || + test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif $rmforce; then + continue + fi + + rmfiles=$file + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if func_lalib_p "$file"; then + func_source $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + func_append rmfiles " $odir/$n" + done + test -n "$old_library" && func_append rmfiles " $odir/$old_library" + + case $opt_mode in + clean) + case " $library_names " in + *" $dlname "*) ;; + *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; + esac + test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if func_lalib_p "$file"; then + + # Read the .lo file + func_source $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" && test none != "$pic_object"; then + func_append rmfiles " $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" && test none != "$non_pic_object"; then + func_append rmfiles " $dir/$non_pic_object" + fi + fi + ;; + + *) + if test clean = "$opt_mode"; then + noexename=$name + case $file in + *.exe) + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result + # $file with .exe has already been added to rmfiles, + # add $file without .exe + func_append rmfiles " $file" + ;; + esac + # Do a test to see if this is a libtool program. + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + func_append rmfiles " $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + func_append rmfiles " $odir/$name $odir/${name}S.$objext" + if test yes = "$fast_install" && test -n "$relink_command"; then + func_append rmfiles " $odir/lt-$name" + fi + if test "X$noexename" != "X$name"; then + func_append rmfiles " $odir/lt-$noexename.c" + fi + fi + fi + ;; + esac + func_show_eval "$RM $rmfiles" 'exit_status=1' + done + + # Try to remove the $objdir's in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + func_show_eval "rmdir $dir >/dev/null 2>&1" + fi + done + + exit $exit_status +} + +if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then + func_mode_uninstall ${1+"$@"} +fi + +test -z "$opt_mode" && { + help=$generic_help + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode '$opt_mode'" + +if test -n "$exec_cmd"; then + eval exec "$exec_cmd" + exit $EXIT_FAILURE +fi + +exit $exit_status + + +# The TAGs below are defined such that we never get into a situation +# where we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: diff --git a/ext/libpqxx-7.7.3/config/m4/Makefile.am b/ext/libpqxx-7.7.3/config/m4/Makefile.am new file mode 100644 index 000000000..c5dd1572f --- /dev/null +++ b/ext/libpqxx-7.7.3/config/m4/Makefile.am @@ -0,0 +1,3 @@ +MAINTAINERCLEANFILES=Makefile.in config.guess config.sub install-sh \ + ltmain.sh missing mkinstalldirs + diff --git a/ext/libpqxx-7.7.3/config/m4/libtool.m4 b/ext/libpqxx-7.7.3/config/m4/libtool.m4 new file mode 100644 index 000000000..c4c02946d --- /dev/null +++ b/ext/libpqxx-7.7.3/config/m4/libtool.m4 @@ -0,0 +1,8394 @@ +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program or library that is built +# using GNU Libtool, you may include this file under the same +# distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +]) + +# serial 58 LT_INIT + + +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) + + +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) + + +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK +AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS=$ltmain + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +_LT_SETUP + +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) + + +# _LT_PREPARE_CC_BASENAME +# ----------------------- +m4_defun([_LT_PREPARE_CC_BASENAME], [ +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in @S|@*""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} +])# _LT_PREPARE_CC_BASENAME + + +# _LT_CC_BASENAME(CC) +# ------------------- +# It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, +# but that macro is also expanded into generated libtool script, which +# arranges for $SED and $ECHO to be set by different means. +m4_defun([_LT_CC_BASENAME], +[m4_require([_LT_PREPARE_CC_BASENAME])dnl +AC_REQUIRE([_LT_DECL_SED])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl +func_cc_basename $1 +cc_basename=$func_cc_basename_result +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl + +_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl +dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl +dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_WITH_SYSROOT])dnl +m4_require([_LT_CMD_TRUNCATE])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options that allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi + +_LT_CHECK_OBJDIR + +m4_require([_LT_TAG_COMPILER])dnl + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld=$lt_cv_prog_gnu_ld + +old_CC=$CC +old_CFLAGS=$CFLAGS + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +_LT_CC_BASENAME([$compiler]) + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + _LT_PATH_MAGIC + fi + ;; +esac + +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP + + +# _LT_PREPARE_SED_QUOTE_VARS +# -------------------------- +# Define a few sed substitution that help us do robust quoting. +m4_defun([_LT_PREPARE_SED_QUOTE_VARS], +[# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([["`\\]]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' +]) + +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from 'configure', and 'config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# 'config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain=$ac_aux_dir/ltmain.sh +])# _LT_PROG_LTMAIN + + +## ------------------------------------- ## +## Accumulate code for creating libtool. ## +## ------------------------------------- ## + +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the 'libtool' +# label. + + +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) + + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) +]) + + +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) + + + +## ------------------------ ## +## FIXME: Eliminate VARNAME ## +## ------------------------ ## + + +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) + + +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) + + +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) + + +# _LT_CONFIG_STATUS_DECLARE([VARNAME]) +# ------------------------------------ +# Quote a variable value, and forward it to 'config.status' so that its +# declaration there will have the same value as in 'configure'. VARNAME +# must have a single quote delimited value for this to work. +m4_define([_LT_CONFIG_STATUS_DECLARE], +[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) + + +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags='_LT_TAGS'dnl +]) + + +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) + + +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) + + +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into 'config.status', and then the shell code to quote escape them in +# for loops in 'config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$[]1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +_LT_OUTPUT_LIBTOOL_INIT +]) + +# _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) +# ------------------------------------ +# Generate a child script FILE with all initialization necessary to +# reuse the environment learned by the parent script, and make the +# file executable. If COMMENT is supplied, it is inserted after the +# '#!' sequence but before initialization text begins. After this +# macro, additional text can be appended to FILE to form the body of +# the child script. The macro ends with non-zero status if the +# file could not be fully written (such as if the disk is full). +m4_ifdef([AS_INIT_GENERATED], +[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], +[m4_defun([_LT_GENERATED_FILE_INIT], +[m4_require([AS_PREPARE])]dnl +[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl +[lt_write_fail=0 +cat >$1 <<_ASEOF || lt_write_fail=1 +#! $SHELL +# Generated by $as_me. +$2 +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$1 <<\_ASEOF || lt_write_fail=1 +AS_SHELL_SANITIZE +_AS_PREPARE +exec AS_MESSAGE_FD>&1 +_ASEOF +test 0 = "$lt_write_fail" && chmod +x $1[]dnl +m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT + +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +_LT_GENERATED_FILE_INIT(["$CONFIG_LT"], +[# Run this file to recreate a libtool stub with the current configuration.]) + +cat >>"$CONFIG_LT" <<\_LTEOF +lt_cl_silent=false +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +'$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to ." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2011 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." + +while test 0 != $[#] +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try '$[0] --help' for more information.]) ;; + + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try '$[0] --help' for more information.]) ;; + esac + shift +done + +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null +fi +_LTEOF + +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +lt_cl_success=: +test yes = "$silent" && + lt_config_lt_args="$lt_config_lt_args --quiet" +exec AS_MESSAGE_LOG_FD>/dev/null +$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false +exec AS_MESSAGE_LOG_FD>>config.log +$lt_cl_success || AS_EXIT(1) +])# LT_OUTPUT + + +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options that allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST + fi + + cfgfile=${ofile}T + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL +# Generated automatically by $as_me ($PACKAGE) $VERSION +# NOTE: Changes made to this file will be lost: look at ltmain.sh. + +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit, 1996 + +_LT_COPYING +_LT_LIBTOOL_TAGS + +# Configured defaults for sys_lib_dlsearch_path munging. +: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} + +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG + +_LT_EOF + + cat <<'_LT_EOF' >> "$cfgfile" + +# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE + +_LT_PREPARE_MUNGE_PATH_LIST +_LT_PREPARE_CC_BASENAME + +# ### END FUNCTIONS SHARED WITH CONFIGURE + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG + + +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Go], [_LT_LANG(GO)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) +# ------------------ +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG + + +m4_ifndef([AC_PROG_GO], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_GO. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ +m4_defun([AC_PROG_GO], +[AC_LANG_PUSH(Go)dnl +AC_ARG_VAR([GOC], [Go compiler command])dnl +AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl +_AC_ARG_VAR_LDFLAGS()dnl +AC_CHECK_TOOL(GOC, gccgo) +if test -z "$GOC"; then + if test -n "$ac_tool_prefix"; then + AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) + fi +fi +if test -z "$GOC"; then + AC_CHECK_PROG(GOC, gccgo, gccgo, false) +fi +])#m4_defun +])#m4_ifndef + + +# _LT_LANG_DEFAULT_CONFIG +# ----------------------- +m4_defun([_LT_LANG_DEFAULT_CONFIG], +[AC_PROVIDE_IFELSE([AC_PROG_CXX], + [LT_LANG(CXX)], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) + +AC_PROVIDE_IFELSE([AC_PROG_F77], + [LT_LANG(F77)], + [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) + +AC_PROVIDE_IFELSE([AC_PROG_FC], + [LT_LANG(FC)], + [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) + +dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal +dnl pulling things in needlessly. +AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([LT_PROG_GCJ], + [LT_LANG(GCJ)], + [m4_ifdef([AC_PROG_GCJ], + [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([A][M_PROG_GCJ], + [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([LT_PROG_GCJ], + [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) + +AC_PROVIDE_IFELSE([AC_PROG_GO], + [LT_LANG(GO)], + [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) + +AC_PROVIDE_IFELSE([LT_PROG_RC], + [LT_LANG(RC)], + [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) +])# _LT_LANG_DEFAULT_CONFIG + +# Obsolete macros: +AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_CXX], []) +dnl AC_DEFUN([AC_LIBTOOL_F77], []) +dnl AC_DEFUN([AC_LIBTOOL_FC], []) +dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) +dnl AC_DEFUN([AC_LIBTOOL_RC], []) + + +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER + + +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE + + +# _LT_LINKER_BOILERPLATE +# ---------------------- +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE + +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ + case $host_os in + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "$LT_MULTI_MODULE"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test 0 = "$_lt_result"; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS=$save_LDFLAGS + ]) + + AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], + [lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD + echo "$AR cr libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cr libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD + $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[912]]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[[012]][[,.]]*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + 10.*|11.*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test yes = "$lt_cv_apple_cc_single_mod"; then + _lt_dar_single_mod='$single_module' + fi + if test yes = "$lt_cv_ld_exported_symbols_list"; then + _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' + fi + if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac +]) + + +# _LT_DARWIN_LINKER_FEATURES([TAG]) +# --------------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + if test yes = "$lt_cv_ld_force_load"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], + [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined + case $cc_basename in + ifort*|nagfor*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test yes = "$_lt_dar_can_shared"; then + output_verbose_link_cmd=func_echo_all + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + m4_if([$1], [CXX], +[ if test yes != "$lt_cv_apple_cc_single_mod"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" + fi +],[]) + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi +]) + +# _LT_SYS_MODULE_PATH_AIX([TAGNAME]) +# ---------------------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +# Store the results from the different compilers for each TAGNAME. +# Allow to override them for all tags through lt_cv_aix_libpath. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], + [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ + lt_aix_libpath_sed='[ + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }]' + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi],[]) + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib + fi + ]) + aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) +fi +])# _LT_SYS_MODULE_PATH_AIX + + +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[m4_divert_text([M4SH-INIT], [$1 +])])# _LT_SHELL_INIT + + + +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Find how we can fake an echo command that does not interpret backslash. +# In particular, with Autoconf 2.60 or later we add some code to the start +# of the generated configure script that will find a shell with a builtin +# printf (that we can use as an echo command). +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +AC_MSG_CHECKING([how to print strings]) +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$[]1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +case $ECHO in + printf*) AC_MSG_RESULT([printf]) ;; + print*) AC_MSG_RESULT([print -r]) ;; + *) AC_MSG_RESULT([cat]) ;; +esac + +m4_ifdef([_AS_DETECT_SUGGESTED], +[_AS_DETECT_SUGGESTED([ + test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test "X`printf %s $ECHO`" = "X$ECHO" \ + || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) + +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) +])# _LT_PROG_ECHO_BACKSLASH + + +# _LT_WITH_SYSROOT +# ---------------- +AC_DEFUN([_LT_WITH_SYSROOT], +[AC_MSG_CHECKING([for sysroot]) +AC_ARG_WITH([sysroot], +[AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], + [Search for dependent libraries within DIR (or the compiler's sysroot + if not specified).])], +[], [with_sysroot=no]) + +dnl lt_sysroot will always be passed unquoted. We quote it here +dnl in case the user passed a directory name. +lt_sysroot= +case $with_sysroot in #( + yes) + if test yes = "$GCC"; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + AC_MSG_RESULT([$with_sysroot]) + AC_MSG_ERROR([The sysroot must be an absolute path.]) + ;; +esac + + AC_MSG_RESULT([${lt_sysroot:-no}]) +_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl +[dependent libraries, and where our libraries should be installed.])]) + +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test no = "$enable_libtool_lock" || enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out what ABI is being produced by ac_compile, and set mode + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE=32 + ;; + *ELF-64*) + HPUX_IA64_MODE=64 + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test yes = "$lt_cv_prog_gnu_ld"; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +mips64*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + emul=elf + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + emul="${emul}32" + ;; + *64-bit*) + emul="${emul}64" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *MSB*) + emul="${emul}btsmip" + ;; + *LSB*) + emul="${emul}ltsmip" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *N32*) + emul="${emul}n32" + ;; + esac + LD="${LD-ld} -m $emul" + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. Note that the listed cases only cover the + # situations where additional linker options are needed (such as when + # doing 32-bit compilation for a host where ld defaults to 64-bit, or + # vice versa); the common cases where no linker options are needed do + # not appear in the list. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*linux*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*linux*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test yes != "$lt_cv_cc_needs_belf"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS=$SAVE_CFLAGS + fi + ;; +*-*solaris*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*|x86_64-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD=${LD-ld}_sol2 + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks=$enable_libtool_lock +])# _LT_ENABLE_LOCK + + +# _LT_PROG_AR +# ----------- +m4_defun([_LT_PROG_AR], +[AC_CHECK_TOOLS(AR, [ar], false) +: ${AR=ar} +: ${AR_FLAGS=cr} +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) + +AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], + [lt_cv_ar_at_file=no + AC_COMPILE_IFELSE([AC_LANG_PROGRAM], + [echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([lt_ar_try]) + if test 0 -eq "$ac_status"; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + AC_TRY_EVAL([lt_ar_try]) + if test 0 -ne "$ac_status"; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + ]) + ]) + +if test no = "$lt_cv_ar_at_file"; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi +_LT_DECL([], [archiver_list_spec], [1], + [How to feed a file listing to the archiver]) +])# _LT_PROG_AR + + +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[_LT_PROG_AR + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) + +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + bitrig* | openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +_LT_DECL([], [lock_old_archive_extraction], [0], + [Whether to use a lock for old archive extraction]) +])# _LT_CMD_OLD_ARCHIVE + + +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) + +if test yes = "[$]$2"; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS +]) + +if test yes = "[$]$2"; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring=ABCD + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test X`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test 17 != "$i" # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac +]) +if test -n "$lt_cv_sys_max_cmd_len"; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN + +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) + + +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN + + +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test yes = "$cross_compiling"; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisibility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF + + +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test yes != "$enable_dlopen"; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen=load_add_on + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen=LoadLibrary + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ + lt_cv_dlopen=dyld + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + tpf*) + # Don't try to run any link tests for TPF. We know it's impossible + # because TPF is a cross-compiler, and we know how we open DSOs. + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + lt_cv_dlopen_self=no + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen=shl_load], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen=dlopen], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test no = "$lt_cv_dlopen"; then + enable_dlopen=no + else + enable_dlopen=yes + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS=$CPPFLAGS + test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS=$LDFLAGS + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS=$LIBS + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test yes = "$lt_cv_dlopen_self"; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS=$save_CPPFLAGS + LDFLAGS=$save_LDFLAGS + LIBS=$save_LIBS + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) + + +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O + + +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) + +hard_links=nottested +if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test no = "$hard_links"; then + AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS + + +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", + [Define to the sub-directory where libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR + + +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then + + # We can hardcode non-existent directories. + if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && + test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) + +if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || + test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then + # Fast installation is not supported + enable_fast_install=no +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH + + +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP"; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB + + +# _LT_PREPARE_MUNGE_PATH_LIST +# --------------------------- +# Make sure func_munge_path_list() is defined correctly. +m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], +[[# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x@S|@2 in + x) + ;; + *:) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" + ;; + x:*) + eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" + ;; + *) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + esac +} +]])# _LT_PREPARE_PATH_LIST + + +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test yes = "$GCC"; then + case $host_os in + darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; + *) lt_awk_arg='/^libraries:/' ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; + *) lt_sed_strip_eq='s|=/|/|g' ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary... + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + # ...but if some path component already ends with the multilib dir we assume + # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). + case "$lt_multi_os_dir; $lt_search_path_spec " in + "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) + lt_multi_os_dir= + ;; + esac + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" + elif test -n "$lt_multi_os_dir"; then + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS = " "; FS = "/|\n";} { + lt_foo = ""; + lt_count = 0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo = "/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=.so +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +AC_ARG_VAR([LT_SYS_LIBRARY_PATH], +[User-defined run-time library search path.]) + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='$libname$release$shared_ext$major' + ;; + +aix[[4-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test ia64 = "$host_cpu"; then + # AIX 5 supports IA64 + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a[(]lib.so.V[)]' + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='$libname$shared_ext' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[[45]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec=$LIB + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[[23]].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 + fi + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[[3-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +linux*android*) + version_type=none # Android doesn't support versioned libraries. + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], + [lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [lt_cv_shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + ]) + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd* | bitrig*) + version_type=sunos + sys_lib_dlsearch_path_spec=/usr/lib + need_lib_prefix=no + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +os2*) + libname_spec='$name' + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test yes = "$with_gnu_ld"; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=sco + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test yes = "$with_gnu_ld"; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test no = "$dynamic_linker" && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test yes = "$GCC"; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec +fi + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec +fi + +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [install_override_mode], [1], + [Permission mode override for installation of shared libraries]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], + [Detected run-time system search path for libraries]) +_LT_DECL([], [configure_time_lt_sys_library_path], [2], + [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) +])# _LT_SYS_DYNAMIC_LINKER + + +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program that can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$1"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD=$lt_cv_path_MAGIC_CMD + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD + ;; +esac]) +MAGIC_CMD=$lt_cv_path_MAGIC_CMD +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX + +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) + + +# _LT_PATH_MAGIC +# -------------- +# find a file program that can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC + + +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PROG_ECHO_BACKSLASH])dnl + +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test no = "$withval" || with_gnu_ld=yes], + [with_gnu_ld=no])dnl + +ac_prog=ld +if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return, which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD=$ac_prog + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test yes = "$with_gnu_ld"; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD=$ac_dir/$ac_prog + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i +cat conftest.i conftest.i >conftest2.i +: ${lt_DD:=$DD} +AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], +[if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: +fi]) +rm -f conftest.i conftest2.i conftest.out]) +])# _LT_PATH_DD + + +# _LT_CMD_TRUNCATE +# ---------------- +# find command to truncate a binary pipe +m4_defun([_LT_CMD_TRUNCATE], +[m4_require([_LT_PATH_DD]) +AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], +[printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +lt_cv_truncate_bin= +if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" +fi +rm -f conftest.i conftest2.i conftest.out +test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) +_LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], + [Command to truncate a binary pipe]) +])# _LT_CMD_TRUNCATE + + +# _LT_CHECK_MAGIC_METHOD +# ---------------------- +# how to check for library dependencies +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_MAGIC_METHOD], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +AC_CACHE_CHECK([how to recognize dependent libraries], +lt_cv_deplibs_check_method, +[lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# 'unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# that responds to the $file_magic_cmd with a given extended regex. +# If you have 'file' or equivalent on your system and you're not sure +# whether 'pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[[4-9]]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[[45]]*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + if ( file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd* | bitrig*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +os2*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method = "file_magic"]) +_LT_DECL([], [file_magic_glob], [1], + [How to find potential files when deplibs_check_method = "file_magic"]) +_LT_DECL([], [want_nocaseglob], [1], + [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD + + +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM=$NM +else + lt_nm_to_check=${ac_tool_prefix}nm + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + tmp_nm=$ac_dir/$lt_tmp_nm + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the 'sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty + case $build_os in + mingw*) lt_bad_file=conftest.nm/nofile ;; + *) lt_bad_file=/dev/null ;; + esac + case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in + *$lt_bad_file* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break 2 + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break 2 + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS=$lt_save_ifs + done + : ${lt_cv_path_NM=no} +fi]) +if test no != "$lt_cv_path_NM"; then + NM=$lt_cv_path_NM +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) + case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols -headers" + ;; + *) + DUMPBIN=: + ;; + esac + fi + AC_SUBST([DUMPBIN]) + if test : != "$DUMPBIN"; then + NM=$DUMPBIN + fi +fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl + +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) + +# _LT_CHECK_SHAREDLIB_FROM_LINKLIB +# -------------------------------- +# how to determine the name of the shared library +# associated with a specific link library. +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +m4_require([_LT_DECL_DLLTOOL]) +AC_CACHE_CHECK([how to associate runtime and link libraries], +lt_cv_sharedlib_from_linklib_cmd, +[lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh; + # decide which one to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd=$ECHO + ;; +esac +]) +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + +_LT_DECL([], [sharedlib_from_linklib_cmd], [1], + [Command to associate shared and link libraries]) +])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB + + +# _LT_PATH_MANIFEST_TOOL +# ---------------------- +# locate the manifest tool +m4_defun([_LT_PATH_MANIFEST_TOOL], +[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], + [lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&AS_MESSAGE_LOG_FD + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest*]) +if test yes != "$lt_cv_path_mainfest_tool"; then + MANIFEST_TOOL=: +fi +_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl +])# _LT_PATH_MANIFEST_TOOL + + +# _LT_DLL_DEF_P([FILE]) +# --------------------- +# True iff FILE is a Windows DLL '.def' file. +# Keep in sync with func_dll_def_p in the libtool script +AC_DEFUN([_LT_DLL_DEF_P], +[dnl + test DEF = "`$SED -n dnl + -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace + -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments + -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl + -e q dnl Only consider the first "real" line + $1`" dnl +])# _LT_DLL_DEF_P + + +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM=-lm) + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M + +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) + + +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl + +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test yes = "$GCC"; then + case $cc_basename in + nvcc*) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; + *) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; + esac + + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI + + +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl + +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test ia64 = "$host_cpu"; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Gets list of data symbols to import. + lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" + # Adjust the below global symbol transforms to fixup imported variables. + lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" + lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" + lt_c_name_lib_hook="\ + -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ + -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" +else + # Disable hooks by default. + lt_cv_sys_global_symbol_to_import= + lt_cdecl_hook= + lt_c_name_hook= + lt_c_name_lib_hook= +fi + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n"\ +$lt_cdecl_hook\ +" -e 's/^T .* \(.*\)$/extern int \1();/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ +$lt_c_name_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" + +# Transform an extracted symbol line into symbol name with lib prefix and +# symbol address. +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ +$lt_c_name_lib_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function, + # D for any global variable and I for any imported variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ +" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ +" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ +" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ +" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&AS_MESSAGE_LOG_FD + if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&AS_MESSAGE_LOG_FD && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT@&t@_DLSYM_CONST +#elif defined __osf__ +/* This system does not cope well with relocations in const data. */ +# define LT@&t@_DLSYM_CONST +#else +# define LT@&t@_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT@&t@_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS=conftstm.$ac_objext + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test yes = "$pipe_works"; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], + [Transform the output of nm into a list of symbols to manually relocate]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +_LT_DECL([nm_interface], [lt_cv_nm_interface], [1], + [The name lister interface]) +_LT_DECL([], [nm_file_list_spec], [1], + [Specify filename containing input files for $NM]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test yes = "$GXX"; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + if test ia64 != "$host_cpu"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64, which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test yes = "$GCC"; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' + if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + case $cc_basename in + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64, which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # flang / f18. f95 an alias for gfortran or flang on Debian + flang* | f18* | f95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + *Sun\ F* | *Sun*Fortran*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Intel*\ [[CF]]*Compiler*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + *Portland\ Group*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +case $host_os in + # For platforms that do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" + ;; +esac + +AC_CACHE_CHECK([for $compiler option to produce PIC], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) + +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC + + +# _LT_LINKER_SHLIBS([TAGNAME]) +# ---------------------------- +# See if the linker supports building shared libraries. +m4_defun([_LT_LINKER_SHLIBS], +[AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +m4_if([$1], [CXX], [ + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ' (' and ')$', so one must not match beginning or + # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', + # as well as any symbol that contains 'd'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test yes != "$GCC"; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd* | bitrig*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + esac + + _LT_TAGVAR(ld_shlibs, $1)=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test yes = "$with_gnu_ld"; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; + *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test yes = "$lt_use_gnu_ld_interface"; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='$wl' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test ia64 != "$host_cpu"; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test linux-dietlibc = "$host_os"; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test no = "$tmp_diet" + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + nagfor*) # NAGFOR 5.3 + tmp_sharedflag='-Wl,-shared' ;; + xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + tcc*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' + ;; + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix[[4-9]]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then + aix_use_runtimelinking=yes + break + fi + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # traditional, no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac + + if test yes = "$GCC"; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag="$shared_flag "'$wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + esac + ;; + + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + ;; + + hpux10*) + if test yes,no = "$GCC,$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + + hpux11*) + if test yes,no = "$GCC,$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + m4_if($1, [], [ + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + _LT_LINKER_OPTION([if $CC understands -b], + _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], + [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) + ;; + esac + fi + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], + [lt_cv_irix_exported_symbol], + [save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" + AC_LINK_IFELSE( + [AC_LANG_SOURCE( + [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], + [C++], [[int foo (void) { return 0; }]], + [Fortran 77], [[ + subroutine foo + end]], + [Fortran], [[ + subroutine foo + end]])])], + [lt_cv_irix_exported_symbol=yes], + [lt_cv_irix_exported_symbol=no]) + LDFLAGS=$save_LDFLAGS]) + if test yes = "$lt_cv_irix_exported_symbol"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' + fi + _LT_TAGVAR(link_all_deplibs, $1)=no + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + linux*) + case $cc_basename in + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(ld_shlibs, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *nto* | *qnx*) + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + osf3*) + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test yes = "$GCC"; then + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. GCC discards it without '$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test yes = "$GCC"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test sequent = "$host_vendor"; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + if test sni = "$host_vendor"; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no + +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld + +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) + +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test yes,yes = "$GCC,$enable_shared"; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_CACHE_CHECK([whether -lc should be explicitly linked in], + [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), + [$RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + ]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) + ;; + esac + fi + ;; +esac + +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting $shlibpath_var if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [postlink_cmds], [2], + [Commands necessary for finishing linking programs]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS + + +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC=$CC +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report what library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_CONFIG($1) +fi +AC_LANG_POP +CC=$lt_save_CC +])# _LT_LANG_C_CONFIG + + +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +if test -n "$CXX" && ( test no != "$CXX" && + ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || + (test g++ != "$CXX"))); then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_caught_CXX_error"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test yes = "$GXX"; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi + + if test yes = "$GXX"; then + # Set up default GNU C++ configuration + + LT_PATH_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test yes = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='$wl' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac + + if test yes = "$GXX"; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag=$shared_flag' $wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + # The "-G" linker flag allows undefined symbols. + _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared + # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' + else + # g++ 2.7 appears to require '-G' NOT '-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ + '"$_LT_TAGVAR(old_archive_cmds, $1)" + _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ + '"$_LT_TAGVAR(reload_cmds, $1)" + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no + + _LT_TAGVAR(GCC, $1)=$GXX + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test yes != "$_lt_caught_CXX_error" + +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG + + +# _LT_FUNC_STRIPNAME_CNF +# ---------------------- +# func_stripname_cnf prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# +# This function is identical to the (non-XSI) version of func_stripname, +# except this one can be used by m4 code that may be executed by configure, +# rather than the libtool script. +m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl +AC_REQUIRE([_LT_DECL_SED]) +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) +func_stripname_cnf () +{ + case @S|@2 in + .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; + *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; + esac +} # func_stripname_cnf +])# _LT_FUNC_STRIPNAME_CNF + + +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= + +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF +package foo +func foo() { +} +_LT_EOF +]) + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case $prev$p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test x-L = "$p" || + test x-R = "$p"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test no = "$pre_test_object_deps_done"; then + case $prev in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)=$prev$p + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test no = "$pre_test_object_deps_done"; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)=$p + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)=$p + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling $1 test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; +esac +]) + +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_LANG_PUSH(Fortran 77) +if test -z "$F77" || test no = "$F77"; then + _lt_disable_F77=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_disable_F77"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${F77-"f77"} + CFLAGS=$FFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)=$G77 + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test yes != "$_lt_disable_F77" + +AC_LANG_POP +])# _LT_LANG_F77_CONFIG + + +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_LANG_PUSH(Fortran) + +if test -z "$FC" || test no = "$FC"; then + _lt_disable_FC=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} + +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_disable_FC"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${FC-"f95"} + CFLAGS=$FCFLAGS + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test yes != "$_lt_disable_FC" + +AC_LANG_POP +])# _LT_LANG_FC_CONFIG + + +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +CFLAGS=$GCJFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)=$LD +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_GO_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Go compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_GO_CONFIG], +[AC_REQUIRE([LT_PROG_GO])dnl +AC_LANG_SAVE + +# Source file extension for Go test sources. +ac_ext=go + +# Object file extension for compiled Go test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="package main; func main() { }" + +# Code to be used in simple link tests +lt_simple_link_test_code='package main; func main() { }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GOC-"gccgo"} +CFLAGS=$GOFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)=$LD +_LT_CC_BASENAME([$compiler]) + +# Go did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GO_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code=$lt_simple_compile_test_code + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +CFLAGS= +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_GO +# ---------- +AC_DEFUN([LT_PROG_GO], +[AC_CHECK_TOOL(GOC, gccgo,) +]) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + +# _LT_DECL_DLLTOOL +# ---------------- +# Ensure DLLTOOL variable is set. +m4_defun([_LT_DECL_DLLTOOL], +[AC_CHECK_TOOL(DLLTOOL, dlltool, false) +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program]) +AC_SUBST([DLLTOOL]) +]) + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f "$lt_ac_sed" && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test 10 -lt "$lt_ac_count" && break + lt_ac_count=`expr $lt_ac_count + 1` + if test "$lt_ac_count" -gt "$lt_ac_max"; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef + +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) + + +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PATH_CONVERSION_FUNCTIONS +# ----------------------------- +# Determine what file name conversion functions should be used by +# func_to_host_file (and, implicitly, by func_to_host_path). These are needed +# for certain cross-compile configurations and native mingw. +m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_MSG_CHECKING([how to convert $build file names to $host format]) +AC_CACHE_VAL(lt_cv_to_host_file_cmd, +[case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac +]) +to_host_file_cmd=$lt_cv_to_host_file_cmd +AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) +_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], + [0], [convert $build file names to $host format])dnl + +AC_MSG_CHECKING([how to convert $build file names to toolchain format]) +AC_CACHE_VAL(lt_cv_to_tool_file_cmd, +[#assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac +]) +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) +_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], + [0], [convert $build files to toolchain format])dnl +])# _LT_PATH_CONVERSION_FUNCTIONS diff --git a/ext/libpqxx-7.7.3/config/m4/ltoptions.m4 b/ext/libpqxx-7.7.3/config/m4/ltoptions.m4 new file mode 100644 index 000000000..94b082976 --- /dev/null +++ b/ext/libpqxx-7.7.3/config/m4/ltoptions.m4 @@ -0,0 +1,437 @@ +# Helper functions for option handling. -*- Autoconf -*- +# +# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software +# Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 8 ltoptions.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) + + +# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) +# ------------------------------------------ +m4_define([_LT_MANGLE_OPTION], +[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) + + +# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) +# --------------------------------------- +# Set option OPTION-NAME for macro MACRO-NAME, and if there is a +# matching handler defined, dispatch to it. Other OPTION-NAMEs are +# saved as a flag. +m4_define([_LT_SET_OPTION], +[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl +m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), + _LT_MANGLE_DEFUN([$1], [$2]), + [m4_warning([Unknown $1 option '$2'])])[]dnl +]) + + +# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) +# ------------------------------------------------------------ +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +m4_define([_LT_IF_OPTION], +[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) + + +# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) +# ------------------------------------------------------- +# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME +# are set. +m4_define([_LT_UNLESS_OPTIONS], +[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), + [m4_define([$0_found])])])[]dnl +m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 +])[]dnl +]) + + +# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) +# ---------------------------------------- +# OPTION-LIST is a space-separated list of Libtool options associated +# with MACRO-NAME. If any OPTION has a matching handler declared with +# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about +# the unknown option and exit. +m4_defun([_LT_SET_OPTIONS], +[# Set options +m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [_LT_SET_OPTION([$1], _LT_Option)]) + +m4_if([$1],[LT_INIT],[ + dnl + dnl Simply set some default values (i.e off) if boolean options were not + dnl specified: + _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no + ]) + _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no + ]) + dnl + dnl If no reference was made to various pairs of opposing options, then + dnl we run the default mode handler for the pair. For example, if neither + dnl 'shared' nor 'disable-shared' was passed, we enable building of shared + dnl archives by default: + _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) + _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], + [_LT_ENABLE_FAST_INSTALL]) + _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], + [_LT_WITH_AIX_SONAME([aix])]) + ]) +])# _LT_SET_OPTIONS + + +## --------------------------------- ## +## Macros to handle LT_INIT options. ## +## --------------------------------- ## + +# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) +# ----------------------------------------- +m4_define([_LT_MANGLE_DEFUN], +[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) + + +# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) +# ----------------------------------------------- +m4_define([LT_OPTION_DEFINE], +[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl +])# LT_OPTION_DEFINE + + +# dlopen +# ------ +LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes +]) + +AU_DEFUN([AC_LIBTOOL_DLOPEN], +[_LT_SET_OPTION([LT_INIT], [dlopen]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'dlopen' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) + + +# win32-dll +# --------- +# Declare package support for building win32 dll's. +LT_OPTION_DEFINE([LT_INIT], [win32-dll], +[enable_win32_dll=yes + +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +esac + +test -z "$AS" && AS=as +_LT_DECL([], [AS], [1], [Assembler program])dnl + +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl + +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl +])# win32-dll + +AU_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +_LT_SET_OPTION([LT_INIT], [win32-dll]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'win32-dll' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) + + +# _LT_ENABLE_SHARED([DEFAULT]) +# ---------------------------- +# implement the --enable-shared flag, and supports the 'shared' and +# 'disable-shared' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_SHARED], +[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) + + _LT_DECL([build_libtool_libs], [enable_shared], [0], + [Whether or not to build shared libraries]) +])# _LT_ENABLE_SHARED + +LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +]) + +AC_DEFUN([AC_DISABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], [disable-shared]) +]) + +AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_SHARED], []) +dnl AC_DEFUN([AM_DISABLE_SHARED], []) + + + +# _LT_ENABLE_STATIC([DEFAULT]) +# ---------------------------- +# implement the --enable-static flag, and support the 'static' and +# 'disable-static' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_STATIC], +[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_static=]_LT_ENABLE_STATIC_DEFAULT) + + _LT_DECL([build_old_libs], [enable_static], [0], + [Whether or not to build static libraries]) +])# _LT_ENABLE_STATIC + +LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +]) + +AC_DEFUN([AC_DISABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], [disable-static]) +]) + +AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_STATIC], []) +dnl AC_DEFUN([AM_DISABLE_STATIC], []) + + + +# _LT_ENABLE_FAST_INSTALL([DEFAULT]) +# ---------------------------------- +# implement the --enable-fast-install flag, and support the 'fast-install' +# and 'disable-fast-install' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_FAST_INSTALL], +[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([fast-install], + [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) + +_LT_DECL([fast_install], [enable_fast_install], [0], + [Whether or not to optimize for fast installation])dnl +])# _LT_ENABLE_FAST_INSTALL + +LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) + +# Old names: +AU_DEFUN([AC_ENABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the 'fast-install' option into LT_INIT's first parameter.]) +]) + +AU_DEFUN([AC_DISABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the 'disable-fast-install' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) +dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) + + +# _LT_WITH_AIX_SONAME([DEFAULT]) +# ---------------------------------- +# implement the --with-aix-soname flag, and support the `aix-soname=aix' +# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT +# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. +m4_define([_LT_WITH_AIX_SONAME], +[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl +shared_archive_member_spec= +case $host,$enable_shared in +power*-*-aix[[5-9]]*,yes) + AC_MSG_CHECKING([which variant of shared library versioning to provide]) + AC_ARG_WITH([aix-soname], + [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], + [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], + [case $withval in + aix|svr4|both) + ;; + *) + AC_MSG_ERROR([Unknown argument to --with-aix-soname]) + ;; + esac + lt_cv_with_aix_soname=$with_aix_soname], + [AC_CACHE_VAL([lt_cv_with_aix_soname], + [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) + with_aix_soname=$lt_cv_with_aix_soname]) + AC_MSG_RESULT([$with_aix_soname]) + if test aix != "$with_aix_soname"; then + # For the AIX way of multilib, we name the shared archive member + # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', + # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. + # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, + # the AIX toolchain works better with OBJECT_MODE set (default 32). + if test 64 = "${OBJECT_MODE-32}"; then + shared_archive_member_spec=shr_64 + else + shared_archive_member_spec=shr + fi + fi + ;; +*) + with_aix_soname=aix + ;; +esac + +_LT_DECL([], [shared_archive_member_spec], [0], + [Shared archive member basename, for filename based shared library versioning on AIX])dnl +])# _LT_WITH_AIX_SONAME + +LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) + + +# _LT_WITH_PIC([MODE]) +# -------------------- +# implement the --with-pic flag, and support the 'pic-only' and 'no-pic' +# LT_INIT options. +# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. +m4_define([_LT_WITH_PIC], +[AC_ARG_WITH([pic], + [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for lt_pkg in $withval; do + IFS=$lt_save_ifs + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [pic_mode=m4_default([$1], [default])]) + +_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl +])# _LT_WITH_PIC + +LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) + +# Old name: +AU_DEFUN([AC_LIBTOOL_PICMODE], +[_LT_SET_OPTION([LT_INIT], [pic-only]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'pic-only' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) + +## ----------------- ## +## LTDL_INIT Options ## +## ----------------- ## + +m4_define([_LTDL_MODE], []) +LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], + [m4_define([_LTDL_MODE], [nonrecursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [recursive], + [m4_define([_LTDL_MODE], [recursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [subproject], + [m4_define([_LTDL_MODE], [subproject])]) + +m4_define([_LTDL_TYPE], []) +LT_OPTION_DEFINE([LTDL_INIT], [installable], + [m4_define([_LTDL_TYPE], [installable])]) +LT_OPTION_DEFINE([LTDL_INIT], [convenience], + [m4_define([_LTDL_TYPE], [convenience])]) diff --git a/ext/libpqxx-7.7.3/config/m4/ltsugar.m4 b/ext/libpqxx-7.7.3/config/m4/ltsugar.m4 new file mode 100644 index 000000000..48bc9344a --- /dev/null +++ b/ext/libpqxx-7.7.3/config/m4/ltsugar.m4 @@ -0,0 +1,124 @@ +# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- +# +# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software +# Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 6 ltsugar.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) + + +# lt_join(SEP, ARG1, [ARG2...]) +# ----------------------------- +# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their +# associated separator. +# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier +# versions in m4sugar had bugs. +m4_define([lt_join], +[m4_if([$#], [1], [], + [$#], [2], [[$2]], + [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) +m4_define([_lt_join], +[m4_if([$#$2], [2], [], + [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) + + +# lt_car(LIST) +# lt_cdr(LIST) +# ------------ +# Manipulate m4 lists. +# These macros are necessary as long as will still need to support +# Autoconf-2.59, which quotes differently. +m4_define([lt_car], [[$1]]) +m4_define([lt_cdr], +[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], + [$#], 1, [], + [m4_dquote(m4_shift($@))])]) +m4_define([lt_unquote], $1) + + +# lt_append(MACRO-NAME, STRING, [SEPARATOR]) +# ------------------------------------------ +# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. +# Note that neither SEPARATOR nor STRING are expanded; they are appended +# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). +# No SEPARATOR is output if MACRO-NAME was previously undefined (different +# than defined and empty). +# +# This macro is needed until we can rely on Autoconf 2.62, since earlier +# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. +m4_define([lt_append], +[m4_define([$1], + m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) + + + +# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) +# ---------------------------------------------------------- +# Produce a SEP delimited list of all paired combinations of elements of +# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list +# has the form PREFIXmINFIXSUFFIXn. +# Needed until we can rely on m4_combine added in Autoconf 2.62. +m4_define([lt_combine], +[m4_if(m4_eval([$# > 3]), [1], + [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl +[[m4_foreach([_Lt_prefix], [$2], + [m4_foreach([_Lt_suffix], + ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, + [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) + + +# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) +# ----------------------------------------------------------------------- +# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited +# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. +m4_define([lt_if_append_uniq], +[m4_ifdef([$1], + [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], + [lt_append([$1], [$2], [$3])$4], + [$5])], + [lt_append([$1], [$2], [$3])$4])]) + + +# lt_dict_add(DICT, KEY, VALUE) +# ----------------------------- +m4_define([lt_dict_add], +[m4_define([$1($2)], [$3])]) + + +# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) +# -------------------------------------------- +m4_define([lt_dict_add_subkey], +[m4_define([$1($2:$3)], [$4])]) + + +# lt_dict_fetch(DICT, KEY, [SUBKEY]) +# ---------------------------------- +m4_define([lt_dict_fetch], +[m4_ifval([$3], + m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), + m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) + + +# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) +# ----------------------------------------------------------------- +m4_define([lt_if_dict_fetch], +[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], + [$5], + [$6])]) + + +# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) +# -------------------------------------------------------------- +m4_define([lt_dict_filter], +[m4_if([$5], [], [], + [lt_join(m4_quote(m4_default([$4], [[, ]])), + lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), + [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl +]) diff --git a/ext/libpqxx-7.7.3/config/m4/ltversion.m4 b/ext/libpqxx-7.7.3/config/m4/ltversion.m4 new file mode 100644 index 000000000..fa04b52a3 --- /dev/null +++ b/ext/libpqxx-7.7.3/config/m4/ltversion.m4 @@ -0,0 +1,23 @@ +# ltversion.m4 -- version numbers -*- Autoconf -*- +# +# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# @configure_input@ + +# serial 4179 ltversion.m4 +# This file is part of GNU Libtool + +m4_define([LT_PACKAGE_VERSION], [2.4.6]) +m4_define([LT_PACKAGE_REVISION], [2.4.6]) + +AC_DEFUN([LTVERSION_VERSION], +[macro_version='2.4.6' +macro_revision='2.4.6' +_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) +_LT_DECL(, macro_revision, 0) +]) diff --git a/ext/libpqxx-7.7.3/config/m4/lt~obsolete.m4 b/ext/libpqxx-7.7.3/config/m4/lt~obsolete.m4 new file mode 100644 index 000000000..c6b26f88f --- /dev/null +++ b/ext/libpqxx-7.7.3/config/m4/lt~obsolete.m4 @@ -0,0 +1,99 @@ +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software +# Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 5 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) +m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) +m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) +m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) +m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) +m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) +m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) diff --git a/ext/libpqxx-7.7.3/config/missing b/ext/libpqxx-7.7.3/config/missing new file mode 100755 index 000000000..1fe1611f1 --- /dev/null +++ b/ext/libpqxx-7.7.3/config/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 1996-2021 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=https://www.perl.org/ +flex_URL=https://github.com/westes/flex +gnu_software_URL=https://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/ext/libpqxx-7.7.3/config/mkinstalldirs b/ext/libpqxx-7.7.3/config/mkinstalldirs new file mode 100755 index 000000000..6b3b5fc5d --- /dev/null +++ b/ext/libpqxx-7.7.3/config/mkinstalldirs @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +# $Id$ + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/ext/libpqxx-7.7.3/config/test-driver b/ext/libpqxx-7.7.3/config/test-driver new file mode 100755 index 000000000..8e575b017 --- /dev/null +++ b/ext/libpqxx-7.7.3/config/test-driver @@ -0,0 +1,148 @@ +#! /bin/sh +# test-driver - basic testsuite driver script. + +scriptversion=2013-07-13.22; # UTC + +# Copyright (C) 2011-2014 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +# Make unconditional expansion of undefined variables an error. This +# helps a lot in preventing typo-related bugs. +set -u + +usage_error () +{ + echo "$0: $*" >&2 + print_usage >&2 + exit 2 +} + +print_usage () +{ + cat <$log_file 2>&1 +estatus=$? + +if test $enable_hard_errors = no && test $estatus -eq 99; then + tweaked_estatus=1 +else + tweaked_estatus=$estatus +fi + +case $tweaked_estatus:$expect_failure in + 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; + 0:*) col=$grn res=PASS recheck=no gcopy=no;; + 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; + 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; + *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; + *:*) col=$red res=FAIL recheck=yes gcopy=yes;; +esac + +# Report the test outcome and exit status in the logs, so that one can +# know whether the test passed or failed simply by looking at the '.log' +# file, without the need of also peaking into the corresponding '.trs' +# file (automake bug#11814). +echo "$res $test_name (exit status: $estatus)" >>$log_file + +# Report outcome to console. +echo "${col}${res}${std}: $test_name" + +# Register the test result, and other relevant metadata. +echo ":test-result: $res" > $trs_file +echo ":global-test-result: $res" >> $trs_file +echo ":recheck: $recheck" >> $trs_file +echo ":copy-in-global-log: $gcopy" >> $trs_file + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/ext/libpqxx-7.7.3/configitems b/ext/libpqxx-7.7.3/configitems new file mode 100644 index 000000000..e6f3925ef --- /dev/null +++ b/ext/libpqxx-7.7.3/configitems @@ -0,0 +1,26 @@ +PACKAGE internal autotools +PACKAGE_BUGREPORT internal autotools +PACKAGE_NAME internal autotools +PACKAGE_STRING internal autotools +PACKAGE_TARNAME internal autotools +PACKAGE_VERSION internal autotools +PQXX_HAVE_CHARCONV_INT internal compiler +PQXX_HAVE_CHARCONV_FLOAT internal compiler +PQXX_HAVE_CMP public compiler +PQXX_HAVE_CONCEPTS public compiler +PQXX_HAVE_CXA_DEMANGLE internal compiler +PQXX_HAVE_GCC_PURE public compiler +PQXX_HAVE_GCC_VISIBILITY public compiler +PQXX_HAVE_LIKELY public compiler +PQXX_HAVE_MULTIDIMENSIONAL_SUBSCRIPT public compiler +PQXX_HAVE_PATH public compiler +PQXX_HAVE_POLL internal compiler +PQXX_HAVE_PQENCRYPTPASSWORDCONN internal libpq +PQXX_HAVE_PQ_PIPELINE internal libpq +PQXX_HAVE_SLEEP_FOR internal compiler +PQXX_HAVE_SPAN public compiler +PQXX_HAVE_STRERROR_R internal compiler +PQXX_HAVE_STRERROR_S internal compiler +PQXX_HAVE_THREAD_LOCAL internal compiler +PQXX_HAVE_YEAR_MONTH_DAY public compiler +VERSION internal autotools diff --git a/ext/libpqxx-7.7.3/configure b/ext/libpqxx-7.7.3/configure new file mode 100755 index 000000000..4c58f19c2 --- /dev/null +++ b/ext/libpqxx-7.7.3/configure @@ -0,0 +1,20443 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for libpqxx 7.7.3. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 + + test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ + || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and Jeroen T. +$0: Vermeulen about your system, including any error +$0: possibly output before this message. Then install a +$0: modern shell, or manually run the script under such a +$0: shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + +SHELL=${CONFIG_SHELL-/bin/sh} + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='libpqxx' +PACKAGE_TARNAME='libpqxx' +PACKAGE_VERSION='7.7.3' +PACKAGE_STRING='libpqxx 7.7.3' +PACKAGE_BUGREPORT='Jeroen T. Vermeulen' +PACKAGE_URL='' + +ac_unique_file="src/connection.cxx" +ac_default_prefix=/usr/local +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +with_postgres_lib +POSTGRES_INCLUDE +PG_CONFIG +PKG_CONFIG +MAINT +MAINTAINER_MODE_FALSE +MAINTAINER_MODE_TRUE +BUILD_REFERENCE_FALSE +BUILD_REFERENCE_TRUE +HAVE_DOT +DOXYGEN +MKDIR +CXXCPP +CPP +LT_SYS_LIBRARY_PATH +OTOOL64 +OTOOL +LIPO +NMEDIT +DSYMUTIL +MANIFEST_TOOL +RANLIB +ac_ct_AR +AR +DLLTOOL +OBJDUMP +LN_S +NM +ac_ct_DUMPBIN +DUMPBIN +LD +FGREP +EGREP +GREP +SED +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +ac_ct_CC +CFLAGS +CC +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +LIBTOOL +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CXX +CPPFLAGS +LDFLAGS +CXXFLAGS +CXX +PQXX_ABI +PQXXVERSION +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +CSCOPE +ETAGS +CTAGS +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +runstatedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL +am__quote' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_silent_rules +enable_dependency_tracking +enable_shared +enable_static +with_pic +enable_fast_install +with_aix_soname +with_gnu_ld +with_sysroot +enable_libtool_lock +enable_documentation +enable_maintainer_mode +enable_audit +enable_suggest +with_postgres_include +with_postgres_lib +' + ac_precious_vars='build_alias +host_alias +target_alias +CXX +CXXFLAGS +LDFLAGS +LIBS +CPPFLAGS +CCC +CC +CFLAGS +LT_SYS_LIBRARY_PATH +CPP +CXXCPP +DOXYGEN +HAVE_DOT' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir runstatedir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures libpqxx 7.7.3 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/libpqxx] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of libpqxx 7.7.3:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --enable-shared[=PKGS] build shared libraries [default=no] + --enable-static[=PKGS] build static libraries [default=yes] + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) + --enable-documentation Generate documentation + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer + + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use + both] + --with-aix-soname=aix|svr4|both + shared library versioning (aka "SONAME") variant to + provide on AIX, [default=aix]. + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + --with-sysroot[=DIR] Search for dependent libraries within DIR (or the + compiler's sysroot if not specified). + --with-postgres-include=DIR + Use PostgreSQL includes from DIR. Defaults to + querying pg_config or pkg-config, whichever is + available. + --with-postgres-lib=DIR Use PostgreSQL libraries from DIR. Defaults to + querying pg_config. + +Some influential environment variables: + CXX C++ compiler command + CXXFLAGS C++ compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CC C compiler command + CFLAGS C compiler flags + LT_SYS_LIBRARY_PATH + User-defined run-time library search path. + CPP C preprocessor + CXXCPP C++ preprocessor + DOXYGEN Path to doxygen needed to build reference documentation + HAVE_DOT Variable used by doxygen to declare availibility of dot + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +libpqxx configure 7.7.3 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_cxx_try_cpp LINENO +# ------------------------ +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_cpp + +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_link + +# ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES +# --------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_cxx_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## ---------------------------------- ## +## Report this to Jeroen T. Vermeulen ## +## ---------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_cxx_check_header_mongrel +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by libpqxx $as_me 7.7.3, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + +ac_aux_dir= +for ac_dir in config "$srcdir"/config; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in config \"$srcdir\"/config" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + +am__api_version='1.16' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +if test x"${MISSING+set}" != xset; then + MISSING="\${SHELL} '$am_aux_dir/missing'" +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='libpqxx' + VERSION='7.7.3' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + +# Variables for tags utilities; see am/tags.am +if test -z "$CTAGS"; then + CTAGS=ctags +fi + +if test -z "$ETAGS"; then + ETAGS=etags +fi + +if test -z "$CSCOPE"; then + CSCOPE=cscope +fi + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + + +PQXX_ABI=7.7 +PQXXVERSION=$PACKAGE_VERSION + + + +ac_config_headers="$ac_config_headers include/pqxx/config.h" + + +# Default prefix for installs. + + + +# Read test programme from config-test. + + + +# Checks for programs. +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 +$as_echo_n "checking whether the C++ compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C++ compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 +$as_echo_n "checking for C++ compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 +$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; } +cat > confinc.mk << 'END' +am__doit: + @echo this is the am__doit target >confinc.out +.PHONY: am__doit +END +am__include="#" +am__quote= +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 + (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + case $?:`cat confinc.out 2>/dev/null` in #( + '0:this is the am__doit target') : + case $s in #( + BSD) : + am__include='.include' am__quote='"' ;; #( + *) : + am__include='include' am__quote='' ;; +esac ;; #( + *) : + ;; +esac + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 +$as_echo "${_am_result}" >&6; } + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + +depcc="$CXX" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CXX_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + + +# Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + enable_shared=no +fi + + + + + + + + + +case `pwd` in + *\ * | *\ *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 +$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; +esac + + + +macro_version='2.4.6' +macro_revision='2.4.6' + + + + + + + + + + + + + +ltmain=$ac_aux_dir/ltmain.sh + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 +$as_echo_n "checking how to print strings... " >&6; } +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "" +} + +case $ECHO in + printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 +$as_echo "printf" >&6; } ;; + print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 +$as_echo "print -r" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 +$as_echo "cat" >&6; } ;; +esac + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if ${ac_cv_path_FGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_FGREP" || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + +test -z "$GREP" && GREP=grep + + + + + + + + + + + + + + + + + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return, which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD=$ac_prog + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test yes = "$with_gnu_ld"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD=$ac_dir/$ac_prog + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 +$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } +if ${lt_cv_path_NM+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM=$NM +else + lt_nm_to_check=${ac_tool_prefix}nm + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + tmp_nm=$ac_dir/$lt_tmp_nm + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the 'sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty + case $build_os in + mingw*) lt_bad_file=conftest.nm/nofile ;; + *) lt_bad_file=/dev/null ;; + esac + case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in + *$lt_bad_file* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break 2 + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break 2 + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS=$lt_save_ifs + done + : ${lt_cv_path_NM=no} +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 +$as_echo "$lt_cv_path_NM" >&6; } +if test no != "$lt_cv_path_NM"; then + NM=$lt_cv_path_NM +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + if test -n "$ac_tool_prefix"; then + for ac_prog in dumpbin "link -dump" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DUMPBIN"; then + ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DUMPBIN=$ac_cv_prog_DUMPBIN +if test -n "$DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 +$as_echo "$DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$DUMPBIN" && break + done +fi +if test -z "$DUMPBIN"; then + ac_ct_DUMPBIN=$DUMPBIN + for ac_prog in dumpbin "link -dump" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DUMPBIN"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN +if test -n "$ac_ct_DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 +$as_echo "$ac_ct_DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_DUMPBIN" && break +done + + if test "x$ac_ct_DUMPBIN" = x; then + DUMPBIN=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DUMPBIN=$ac_ct_DUMPBIN + fi +fi + + case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols -headers" + ;; + *) + DUMPBIN=: + ;; + esac + fi + + if test : != "$DUMPBIN"; then + NM=$DUMPBIN + fi +fi +test -z "$NM" && NM=nm + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 +$as_echo_n "checking the name lister ($NM) interface... " >&6; } +if ${lt_cv_nm_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 +$as_echo "$lt_cv_nm_interface" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +# find the maximum length of command line arguments +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 +$as_echo_n "checking the maximum length of command line arguments... " >&6; } +if ${lt_cv_sys_max_cmd_len+:} false; then : + $as_echo_n "(cached) " >&6 +else + i=0 + teststring=ABCD + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test X`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test 17 != "$i" # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac + +fi + +if test -n "$lt_cv_sys_max_cmd_len"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 +$as_echo "$lt_cv_sys_max_cmd_len" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 +$as_echo "none" >&6; } +fi +max_cmd_len=$lt_cv_sys_max_cmd_len + + + + + + +: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi + + + + + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 +$as_echo_n "checking how to convert $build file names to $host format... " >&6; } +if ${lt_cv_to_host_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac + +fi + +to_host_file_cmd=$lt_cv_to_host_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 +$as_echo "$lt_cv_to_host_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 +$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } +if ${lt_cv_to_tool_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + #assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac + +fi + +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 +$as_echo "$lt_cv_to_tool_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 +$as_echo_n "checking for $LD option to reload object files... " >&6; } +if ${lt_cv_ld_reload_flag+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_reload_flag='-r' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 +$as_echo "$lt_cv_ld_reload_flag" >&6; } +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + if test yes != "$GCC"; then + reload_cmds=false + fi + ;; + darwin*) + if test yes = "$GCC"; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + +test -z "$OBJDUMP" && OBJDUMP=objdump + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 +$as_echo_n "checking how to recognize dependent libraries... " >&6; } +if ${lt_cv_deplibs_check_method+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# 'unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# that responds to the $file_magic_cmd with a given extended regex. +# If you have 'file' or equivalent on your system and you're not sure +# whether 'pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[4-9]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[45]*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + if ( file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[3-9]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd* | bitrig*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +os2*) + lt_cv_deplibs_check_method=pass_all + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 +$as_echo "$lt_cv_deplibs_check_method" >&6; } + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + + + + + + + + + + + + + + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. +set dummy ${ac_tool_prefix}dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DLLTOOL"; then + ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DLLTOOL=$ac_cv_prog_DLLTOOL +if test -n "$DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 +$as_echo "$DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DLLTOOL"; then + ac_ct_DLLTOOL=$DLLTOOL + # Extract the first word of "dlltool", so it can be a program name with args. +set dummy dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DLLTOOL"; then + ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DLLTOOL="dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL +if test -n "$ac_ct_DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 +$as_echo "$ac_ct_DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DLLTOOL" = x; then + DLLTOOL="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DLLTOOL=$ac_ct_DLLTOOL + fi +else + DLLTOOL="$ac_cv_prog_DLLTOOL" +fi + +test -z "$DLLTOOL" && DLLTOOL=dlltool + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 +$as_echo_n "checking how to associate runtime and link libraries... " >&6; } +if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh; + # decide which one to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd=$ECHO + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 +$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + + + + + + + + +if test -n "$ac_tool_prefix"; then + for ac_prog in ar + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} +: ${AR_FLAGS=cr} + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 +$as_echo_n "checking for archiver @FILE support... " >&6; } +if ${lt_cv_ar_at_file+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ar_at_file=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test 0 -eq "$ac_status"; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test 0 -ne "$ac_status"; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 +$as_echo "$lt_cv_ar_at_file" >&6; } + +if test no = "$lt_cv_ar_at_file"; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +test -z "$STRIP" && STRIP=: + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +test -z "$RANLIB" && RANLIB=: + + + + + + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + bitrig* | openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Check for command to grab the raw symbol name followed by C symbol from nm. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 +$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } +if ${lt_cv_sys_global_symbol_pipe+:} false; then : + $as_echo_n "(cached) " >&6 +else + +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[BCDEGRST]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[BCDT]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[ABCDGISTW]' + ;; +hpux*) + if test ia64 = "$host_cpu"; then + symcode='[ABCDEGRST]' + fi + ;; +irix* | nonstopux*) + symcode='[BCDEGRST]' + ;; +osf*) + symcode='[BCDEGQRST]' + ;; +solaris*) + symcode='[BDRT]' + ;; +sco3.2v5*) + symcode='[DT]' + ;; +sysv4.2uw2*) + symcode='[DT]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[ABDT]' + ;; +sysv4) + symcode='[DFNSTU]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[ABCDGIRSTW]' ;; +esac + +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Gets list of data symbols to import. + lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" + # Adjust the below global symbol transforms to fixup imported variables. + lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" + lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" + lt_c_name_lib_hook="\ + -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ + -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" +else + # Disable hooks by default. + lt_cv_sys_global_symbol_to_import= + lt_cdecl_hook= + lt_c_name_hook= + lt_c_name_lib_hook= +fi + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n"\ +$lt_cdecl_hook\ +" -e 's/^T .* \(.*\)$/extern int \1();/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ +$lt_c_name_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" + +# Transform an extracted symbol line into symbol name with lib prefix and +# symbol address. +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ +$lt_c_name_lib_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function, + # D for any global variable and I for any imported variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK '"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ +" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ +" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ +" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ +" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Now try to grab the symbols. + nlist=conftest.nm + $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&5 + if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&5 && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined __osf__ +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS=conftstm.$ac_objext + CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest$ac_exeext; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test yes = "$pipe_works"; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done + +fi + +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 +$as_echo_n "checking for sysroot... " >&6; } + +# Check whether --with-sysroot was given. +if test "${with_sysroot+set}" = set; then : + withval=$with_sysroot; +else + with_sysroot=no +fi + + +lt_sysroot= +case $with_sysroot in #( + yes) + if test yes = "$GCC"; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 +$as_echo "$with_sysroot" >&6; } + as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 + ;; +esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 +$as_echo "${lt_sysroot:-no}" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 +$as_echo_n "checking for a working dd... " >&6; } +if ${ac_cv_path_lt_DD+:} false; then : + $as_echo_n "(cached) " >&6 +else + printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +: ${lt_DD:=$DD} +if test -z "$lt_DD"; then + ac_path_lt_DD_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in dd; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_lt_DD" || continue +if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: +fi + $ac_path_lt_DD_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_lt_DD"; then + : + fi +else + ac_cv_path_lt_DD=$lt_DD +fi + +rm -f conftest.i conftest2.i conftest.out +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 +$as_echo "$ac_cv_path_lt_DD" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 +$as_echo_n "checking how to truncate binary pipes... " >&6; } +if ${lt_cv_truncate_bin+:} false; then : + $as_echo_n "(cached) " >&6 +else + printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +lt_cv_truncate_bin= +if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" +fi +rm -f conftest.i conftest2.i conftest.out +test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 +$as_echo "$lt_cv_truncate_bin" >&6; } + + + + + + + +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in $*""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} + + +# Check whether --enable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then : + enableval=$enable_libtool_lock; +fi + +test no = "$enable_libtool_lock" || enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out what ABI is being produced by ac_compile, and set mode + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE=32 + ;; + *ELF-64*) + HPUX_IA64_MODE=64 + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + if test yes = "$lt_cv_prog_gnu_ld"; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +mips64*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + emul=elf + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + emul="${emul}32" + ;; + *64-bit*) + emul="${emul}64" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *MSB*) + emul="${emul}btsmip" + ;; + *LSB*) + emul="${emul}ltsmip" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *N32*) + emul="${emul}n32" + ;; + esac + LD="${LD-ld} -m $emul" + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. Note that the listed cases only cover the + # situations where additional linker options are needed (such as when + # doing 32-bit compilation for a host where ld defaults to 64-bit, or + # vice versa); the common cases where no linker options are needed do + # not appear in the list. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*linux*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*linux*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -belf" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 +$as_echo_n "checking whether the C compiler needs -belf... " >&6; } +if ${lt_cv_cc_needs_belf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_cc_needs_belf=yes +else + lt_cv_cc_needs_belf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 +$as_echo "$lt_cv_cc_needs_belf" >&6; } + if test yes != "$lt_cv_cc_needs_belf"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS=$SAVE_CFLAGS + fi + ;; +*-*solaris*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*|x86_64-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD=${LD-ld}_sol2 + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks=$enable_libtool_lock + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. +set dummy ${ac_tool_prefix}mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MANIFEST_TOOL"; then + ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL +if test -n "$MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 +$as_echo "$MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_MANIFEST_TOOL"; then + ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL + # Extract the first word of "mt", so it can be a program name with args. +set dummy mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_MANIFEST_TOOL"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL +if test -n "$ac_ct_MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 +$as_echo "$ac_ct_MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_MANIFEST_TOOL" = x; then + MANIFEST_TOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL + fi +else + MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" +fi + +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 +$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } +if ${lt_cv_path_mainfest_tool+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&5 + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 +$as_echo "$lt_cv_path_mainfest_tool" >&6; } +if test yes != "$lt_cv_path_mainfest_tool"; then + MANIFEST_TOOL=: +fi + + + + + + + case $host_os in + rhapsody* | darwin*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. +set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DSYMUTIL"; then + ac_ct_DSYMUTIL=$DSYMUTIL + # Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DSYMUTIL"; then + ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL +if test -n "$ac_ct_DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 +$as_echo "$ac_ct_DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DSYMUTIL" = x; then + DSYMUTIL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DSYMUTIL=$ac_ct_DSYMUTIL + fi +else + DSYMUTIL="$ac_cv_prog_DSYMUTIL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. +set dummy ${ac_tool_prefix}nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NMEDIT"; then + ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +NMEDIT=$ac_cv_prog_NMEDIT +if test -n "$NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 +$as_echo "$NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_NMEDIT"; then + ac_ct_NMEDIT=$NMEDIT + # Extract the first word of "nmedit", so it can be a program name with args. +set dummy nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_NMEDIT"; then + ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_NMEDIT="nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT +if test -n "$ac_ct_NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 +$as_echo "$ac_ct_NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_NMEDIT" = x; then + NMEDIT=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + NMEDIT=$ac_ct_NMEDIT + fi +else + NMEDIT="$ac_cv_prog_NMEDIT" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. +set dummy ${ac_tool_prefix}lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$LIPO"; then + ac_cv_prog_LIPO="$LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LIPO="${ac_tool_prefix}lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LIPO=$ac_cv_prog_LIPO +if test -n "$LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 +$as_echo "$LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LIPO"; then + ac_ct_LIPO=$LIPO + # Extract the first word of "lipo", so it can be a program name with args. +set dummy lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_LIPO"; then + ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LIPO="lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO +if test -n "$ac_ct_LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 +$as_echo "$ac_ct_LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_LIPO" = x; then + LIPO=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIPO=$ac_ct_LIPO + fi +else + LIPO="$ac_cv_prog_LIPO" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL"; then + ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL="${ac_tool_prefix}otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL=$ac_cv_prog_OTOOL +if test -n "$OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 +$as_echo "$OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL"; then + ac_ct_OTOOL=$OTOOL + # Extract the first word of "otool", so it can be a program name with args. +set dummy otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL"; then + ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL="otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL +if test -n "$ac_ct_OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 +$as_echo "$ac_ct_OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL" = x; then + OTOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL=$ac_ct_OTOOL + fi +else + OTOOL="$ac_cv_prog_OTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL64"; then + ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL64=$ac_cv_prog_OTOOL64 +if test -n "$OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 +$as_echo "$OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL64"; then + ac_ct_OTOOL64=$OTOOL64 + # Extract the first word of "otool64", so it can be a program name with args. +set dummy otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL64"; then + ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL64="otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 +if test -n "$ac_ct_OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 +$as_echo "$ac_ct_OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL64" = x; then + OTOOL64=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL64=$ac_ct_OTOOL64 + fi +else + OTOOL64="$ac_cv_prog_OTOOL64" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 +$as_echo_n "checking for -single_module linker flag... " >&6; } +if ${lt_cv_apple_cc_single_mod+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_apple_cc_single_mod=no + if test -z "$LT_MULTI_MODULE"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&5 + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test 0 = "$_lt_result"; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&5 + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 +$as_echo "$lt_cv_apple_cc_single_mod" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 +$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } +if ${lt_cv_ld_exported_symbols_list+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_ld_exported_symbols_list=yes +else + lt_cv_ld_exported_symbols_list=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 +$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 +$as_echo_n "checking for -force_load linker flag... " >&6; } +if ${lt_cv_ld_force_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 + echo "$AR cr libconftest.a conftest.o" >&5 + $AR cr libconftest.a conftest.o 2>&5 + echo "$RANLIB libconftest.a" >&5 + $RANLIB libconftest.a 2>&5 + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&5 + elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&5 + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 +$as_echo "$lt_cv_ld_force_load" >&6; } + case $host_os in + rhapsody* | darwin1.[012]) + _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[912]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[012][,.]*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + 10.*|11.*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test yes = "$lt_cv_apple_cc_single_mod"; then + _lt_dar_single_mod='$single_module' + fi + if test yes = "$lt_cv_ld_exported_symbols_list"; then + _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' + fi + if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac + +# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x$2 in + x) + ;; + *:) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" + ;; + x:*) + eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" + ;; + *) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" + ;; + esac +} + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in dlfcn.h +do : + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default +" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF + +fi + +done + + + +func_stripname_cnf () +{ + case $2 in + .*) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%\\\\$2\$%%"`;; + *) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%$2\$%%"`;; + esac +} # func_stripname_cnf + + + + + +# Set options + + + + enable_dlopen=no + + + enable_win32_dll=no + + + + # Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + enable_static=yes +fi + + + + + + + + + + +# Check whether --with-pic was given. +if test "${with_pic+set}" = set; then : + withval=$with_pic; lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for lt_pkg in $withval; do + IFS=$lt_save_ifs + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + pic_mode=default +fi + + + + + + + + + # Check whether --enable-fast-install was given. +if test "${enable_fast_install+set}" = set; then : + enableval=$enable_fast_install; p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + enable_fast_install=yes +fi + + + + + + + + + shared_archive_member_spec= +case $host,$enable_shared in +power*-*-aix[5-9]*,yes) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 +$as_echo_n "checking which variant of shared library versioning to provide... " >&6; } + +# Check whether --with-aix-soname was given. +if test "${with_aix_soname+set}" = set; then : + withval=$with_aix_soname; case $withval in + aix|svr4|both) + ;; + *) + as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 + ;; + esac + lt_cv_with_aix_soname=$with_aix_soname +else + if ${lt_cv_with_aix_soname+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_with_aix_soname=aix +fi + + with_aix_soname=$lt_cv_with_aix_soname +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 +$as_echo "$with_aix_soname" >&6; } + if test aix != "$with_aix_soname"; then + # For the AIX way of multilib, we name the shared archive member + # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', + # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. + # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, + # the AIX toolchain works better with OBJECT_MODE set (default 32). + if test 64 = "${OBJECT_MODE-32}"; then + shared_archive_member_spec=shr_64 + else + shared_archive_member_spec=shr + fi + fi + ;; +*) + with_aix_soname=aix + ;; +esac + + + + + + + + + + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS=$ltmain + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +test -z "$LN_S" && LN_S="ln -s" + + + + + + + + + + + + + + +if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 +$as_echo_n "checking for objdir... " >&6; } +if ${lt_cv_objdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 +$as_echo "$lt_cv_objdir" >&6; } +objdir=$lt_cv_objdir + + + + + +cat >>confdefs.h <<_ACEOF +#define LT_OBJDIR "$lt_cv_objdir/" +_ACEOF + + + + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld=$lt_cv_prog_gnu_ld + +old_CC=$CC +old_CFLAGS=$CFLAGS + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +func_cc_basename $compiler +cc_basename=$func_cc_basename_result + + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 +$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/${ac_tool_prefix}file"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD=$lt_cv_path_MAGIC_CMD + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD + ;; +esac +fi + +MAGIC_CMD=$lt_cv_path_MAGIC_CMD +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 +$as_echo_n "checking for file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/file"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD=$lt_cv_path_MAGIC_CMD + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD + ;; +esac +fi + +MAGIC_CMD=$lt_cv_path_MAGIC_CMD +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + else + MAGIC_CMD=: + fi +fi + + fi + ;; +esac + +# Use C for the default configuration in the libtool script + +lt_save_CC=$CC +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +objext=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + +lt_prog_compiler_no_builtin_flag= + +if test yes = "$GCC"; then + case $cc_basename in + nvcc*) + lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; + *) + lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } +if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } + +if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : +fi + +fi + + + + + + + lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= + + + if test yes = "$GCC"; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + lt_prog_compiler_pic='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic='-DDLL_EXPORT' + case $host_os in + os2*) + lt_prog_compiler_static='$wl-static' + ;; + esac + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + ;; + + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi + ;; + + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + lt_prog_compiler_wl='-Xlinker ' + if test -n "$lt_prog_compiler_pic"; then + lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + case $cc_basename in + nagfor*) + # NAG Fortran compiler + lt_prog_compiler_wl='-Wl,-Wl,,' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + esac + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic='-DDLL_EXPORT' + case $host_os in + os2*) + lt_prog_compiler_static='$wl-static' + ;; + esac + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='$wl-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64, which still supported -KPIC. + ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + # flang / f18. f95 an alias for gfortran or flang on Debian + flang* | f18* | f95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='--shared' + lt_prog_compiler_static='--static' + ;; + nagfor*) + # NAG Fortran compiler + lt_prog_compiler_wl='-Wl,-Wl,,' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-qpic' + lt_prog_compiler_static='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='' + ;; + *Sun\ F* | *Sun*Fortran*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Wl,' + ;; + *Intel*\ [CF]*Compiler*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + *Portland\ Group*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + + rdos*) + lt_prog_compiler_static='-non_shared' + ;; + + solaris*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + lt_prog_compiler_wl='-Qoption ld ';; + *) + lt_prog_compiler_wl='-Wl,';; + esac + ;; + + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + unicos*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_can_build_shared=no + ;; + + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi + +case $host_os in + # For platforms that do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic=$lt_prog_compiler_pic +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 +$as_echo "$lt_cv_prog_compiler_pic" >&6; } +lt_prog_compiler_pic=$lt_cv_prog_compiler_pic + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } +if ${lt_cv_prog_compiler_pic_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } + +if test yes = "$lt_cv_prog_compiler_pic_works"; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi + +fi + + + + + + + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works=yes + fi + else + lt_cv_prog_compiler_static_works=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 +$as_echo "$lt_cv_prog_compiler_static_works" >&6; } + +if test yes = "$lt_cv_prog_compiler_static_works"; then + : +else + lt_prog_compiler_static= +fi + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + +hard_links=nottested +if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test no = "$hard_links"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + runpath_var= + allow_undefined_flag= + always_export_symbols=no + archive_cmds= + archive_expsym_cmds= + compiler_needs_object=no + enable_shared_with_static_runtimes=no + export_dynamic_flag_spec= + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + hardcode_automatic=no + hardcode_direct=no + hardcode_direct_absolute=no + hardcode_libdir_flag_spec= + hardcode_libdir_separator= + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + inherit_rpath=no + link_all_deplibs=unknown + module_cmds= + module_expsym_cmds= + old_archive_from_new_cmds= + old_archive_from_expsyms_cmds= + thread_safe_flag_spec= + whole_archive_flag_spec= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ' (' and ')$', so one must not match beginning or + # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', + # as well as any symbol that contains 'd'. + exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test yes != "$GCC"; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd* | bitrig*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs=no + ;; + esac + + ld_shlibs=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test yes = "$with_gnu_ld"; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; + *\ \(GNU\ Binutils\)\ [3-9]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test yes = "$lt_use_gnu_ld_interface"; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='$wl' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + export_dynamic_flag_spec='$wl--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + whole_archive_flag_spec= + fi + supports_anon_versioning=no + case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test ia64 != "$host_cpu"; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + export_dynamic_flag_spec='$wl--export-all-symbols' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs=no + fi + ;; + + haiku*) + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + link_all_deplibs=yes + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + shrext_cmds=.dll + archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes=yes + ;; + + interix[3-9]*) + hardcode_direct=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='$wl-rpath,$libdir' + export_dynamic_flag_spec='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test linux-dietlibc = "$host_os"; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test no = "$tmp_diet" + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + whole_archive_flag_spec= + tmp_sharedflag='--shared' ;; + nagfor*) # NAGFOR 5.3 + tmp_sharedflag='-Wl,-shared' ;; + xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + compiler_needs_object=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + compiler_needs_object=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + + if test yes = "$supports_anon_versioning"; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + tcc*) + export_dynamic_flag_spec='-rdynamic' + ;; + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test yes = "$supports_anon_versioning"; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + ld_shlibs=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test no = "$ld_shlibs"; then + runpath_var= + hardcode_libdir_flag_spec= + export_dynamic_flag_spec= + whole_archive_flag_spec= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix[4-9]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then + aix_use_runtimelinking=yes + break + fi + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds='' + hardcode_direct=yes + hardcode_direct_absolute=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + file_list_spec='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # traditional, no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + hardcode_direct=no + hardcode_direct_absolute=no + ;; + esac + + if test yes = "$GCC"; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag="$shared_flag "'$wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + export_dynamic_flag_spec='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag=' $wl-bernotok' + allow_undefined_flag=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec='$convenience' + fi + archive_cmds_need_lc=yes + archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + bsdi[45]*) + export_dynamic_flag_spec=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + always_export_symbols=yes + file_list_spec='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, )='true' + enable_shared_with_static_runtimes=yes + exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + old_postinstall_cmds='chmod 644 $oldlib' + postlink_cmds='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' + enable_shared_with_static_runtimes=yes + ;; + esac + ;; + + darwin* | rhapsody*) + + + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + if test yes = "$lt_cv_ld_force_load"; then + whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec='' + fi + link_all_deplibs=yes + allow_undefined_flag=$_lt_dar_allow_undefined + case $cc_basename in + ifort*|nagfor*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test yes = "$_lt_dar_can_shared"; then + output_verbose_link_cmd=func_echo_all + archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + + else + ld_shlibs=no + fi + + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + if test yes = "$GCC"; then + archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='$wl+b $wl$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='$wl-E' + ;; + + hpux10*) + if test yes,no = "$GCC,$with_gnu_ld"; then + archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test no = "$with_gnu_ld"; then + hardcode_libdir_flag_spec='$wl+b $wl$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='$wl-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + + hpux11*) + if test yes,no = "$GCC,$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 +$as_echo_n "checking if $CC understands -b... " >&6; } +if ${lt_cv_prog_compiler__b+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler__b=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -b" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler__b=yes + fi + else + lt_cv_prog_compiler__b=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 +$as_echo "$lt_cv_prog_compiler__b" >&6; } + +if test yes = "$lt_cv_prog_compiler__b"; then + archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' +else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' +fi + + ;; + esac + fi + if test no = "$with_gnu_ld"; then + hardcode_libdir_flag_spec='$wl+b $wl$libdir' + hardcode_libdir_separator=: + + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + *) + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='$wl-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test yes = "$GCC"; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 +$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } +if ${lt_cv_irix_exported_symbol+:} false; then : + $as_echo_n "(cached) " >&6 +else + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo (void) { return 0; } +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_irix_exported_symbol=yes +else + lt_cv_irix_exported_symbol=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 +$as_echo "$lt_cv_irix_exported_symbol" >&6; } + if test yes = "$lt_cv_irix_exported_symbol"; then + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' + fi + link_all_deplibs=no + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + hardcode_libdir_separator=: + inherit_rpath=yes + link_all_deplibs=yes + ;; + + linux*) + case $cc_basename in + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + ld_shlibs=yes + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; + + *nto* | *qnx*) + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + hardcode_shlibpath_var=no + hardcode_direct_absolute=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' + hardcode_libdir_flag_spec='$wl-rpath,$libdir' + export_dynamic_flag_spec='$wl-E' + else + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='$wl-rpath,$libdir' + fi + else + ld_shlibs=no + fi + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + shrext_cmds=.dll + archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes=yes + ;; + + osf3*) + if test yes = "$GCC"; then + allow_undefined_flag=' $wl-expect_unresolved $wl\*' + archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test yes = "$GCC"; then + allow_undefined_flag=' $wl-expect_unresolved $wl\*' + archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + archive_cmds_need_lc='no' + hardcode_libdir_separator=: + ;; + + solaris*) + no_undefined_flag=' -z defs' + if test yes = "$GCC"; then + wlarc='$wl' + archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='$wl' + archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. GCC discards it without '$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test yes = "$GCC"; then + whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + else + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' + fi + ;; + esac + link_all_deplibs=yes + ;; + + sunos4*) + if test sequent = "$host_vendor"; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds='$CC -r -o $output$reload_objs' + hardcode_direct=no + ;; + motorola) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag='$wl-z,text' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag='$wl-z,text' + allow_undefined_flag='$wl-z,nodefs' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='$wl-R,$libdir' + hardcode_libdir_separator=':' + link_all_deplibs=yes + export_dynamic_flag_spec='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac + + if test sni = "$host_vendor"; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + export_dynamic_flag_spec='$wl-Blargedynsym' + ;; + esac + fi + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 +$as_echo "$ld_shlibs" >&6; } +test no = "$ld_shlibs" && can_build_shared=no + +with_gnu_ld=$with_gnu_ld + + + + + + + + + + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes + + if test yes,yes = "$GCC,$enable_shared"; then + case $archive_cmds in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + pic_flag=$lt_prog_compiler_pic + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc=no + else + lt_cv_archive_cmds_need_lc=yes + fi + allow_undefined_flag=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc" >&6; } + archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +if test yes = "$GCC"; then + case $host_os in + darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; + *) lt_awk_arg='/^libraries:/' ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; + *) lt_sed_strip_eq='s|=/|/|g' ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary... + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + # ...but if some path component already ends with the multilib dir we assume + # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). + case "$lt_multi_os_dir; $lt_search_path_spec " in + "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) + lt_multi_os_dir= + ;; + esac + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" + elif test -n "$lt_multi_os_dir"; then + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS = " "; FS = "/|\n";} { + lt_foo = ""; + lt_count = 0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo = "/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[lt_foo]++; } + if (lt_freq[lt_foo] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's|/\([A-Za-z]:\)|\1|g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=.so +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + + + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='$libname$release$shared_ext$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test ia64 = "$host_cpu"; then + # AIX 5 supports IA64 + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a(lib.so.V)' + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='$libname$shared_ext' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec=$LIB + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 + fi + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +linux*android*) + version_type=none # Android doesn't support versioned libraries. + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + hardcode_libdir_flag_spec='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd* | bitrig*) + version_type=sunos + sys_lib_dlsearch_path_spec=/usr/lib + need_lib_prefix=no + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +os2*) + libname_spec='$name' + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test yes = "$with_gnu_ld"; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=sco + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test yes = "$with_gnu_ld"; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test no = "$dynamic_linker" && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test yes = "$GCC"; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec +fi + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec +fi + +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || + test -n "$runpath_var" || + test yes = "$hardcode_automatic"; then + + # We can hardcode non-existent directories. + if test no != "$hardcode_direct" && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && + test no != "$hardcode_minus_L"; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 +$as_echo "$hardcode_action" >&6; } + +if test relink = "$hardcode_action" || + test yes = "$inherit_rpath"; then + # Fast installation is not supported + enable_fast_install=no +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + if test yes != "$enable_dlopen"; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen=load_add_on + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen=LoadLibrary + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl +else + + lt_cv_dlopen=dyld + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + tpf*) + # Don't try to run any link tests for TPF. We know it's impossible + # because TPF is a cross-compiler, and we know how we open DSOs. + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + lt_cv_dlopen_self=no + ;; + + *) + ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" +if test "x$ac_cv_func_shl_load" = xyes; then : + lt_cv_dlopen=shl_load +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 +$as_echo_n "checking for shl_load in -ldld... " >&6; } +if ${ac_cv_lib_dld_shl_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (); +int +main () +{ +return shl_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_shl_load=yes +else + ac_cv_lib_dld_shl_load=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 +$as_echo "$ac_cv_lib_dld_shl_load" >&6; } +if test "x$ac_cv_lib_dld_shl_load" = xyes; then : + lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld +else + ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" +if test "x$ac_cv_func_dlopen" = xyes; then : + lt_cv_dlopen=dlopen +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 +$as_echo_n "checking for dlopen in -lsvld... " >&6; } +if ${ac_cv_lib_svld_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_svld_dlopen=yes +else + ac_cv_lib_svld_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 +$as_echo "$ac_cv_lib_svld_dlopen" >&6; } +if test "x$ac_cv_lib_svld_dlopen" = xyes; then : + lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 +$as_echo_n "checking for dld_link in -ldld... " >&6; } +if ${ac_cv_lib_dld_dld_link+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dld_link (); +int +main () +{ +return dld_link (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_dld_link=yes +else + ac_cv_lib_dld_dld_link=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 +$as_echo "$ac_cv_lib_dld_dld_link" >&6; } +if test "x$ac_cv_lib_dld_dld_link" = xyes; then : + lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test no = "$lt_cv_dlopen"; then + enable_dlopen=no + else + enable_dlopen=yes + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS=$CPPFLAGS + test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS=$LDFLAGS + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS=$LIBS + LIBS="$lt_cv_dlopen_libs $LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 +$as_echo_n "checking whether a program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test yes = "$cross_compiling"; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisibility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 +$as_echo "$lt_cv_dlopen_self" >&6; } + + if test yes = "$lt_cv_dlopen_self"; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 +$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self_static+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test yes = "$cross_compiling"; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisibility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 +$as_echo "$lt_cv_dlopen_self_static" >&6; } + fi + + CPPFLAGS=$save_CPPFLAGS + LDFLAGS=$save_LDFLAGS + LIBS=$save_LIBS + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + + + + + + + + + + + + + + + + + +striplib= +old_striplib= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 +$as_echo_n "checking whether stripping libraries is possible... " >&6; } +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP"; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac +fi + + + + + + + + + + + + + # Report what library types will actually be built + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 +$as_echo_n "checking if libtool supports shared libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 +$as_echo "$can_build_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 +$as_echo_n "checking whether to build shared libraries... " >&6; } + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[4-9]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 +$as_echo "$enable_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 +$as_echo_n "checking whether to build static libraries... " >&6; } + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 +$as_echo "$enable_static" >&6; } + + + + +fi +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +CC=$lt_save_CC + + if test -n "$CXX" && ( test no != "$CXX" && + ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || + (test g++ != "$CXX"))); then + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 +$as_echo_n "checking how to run the C++ preprocessor... " >&6; } +if test -z "$CXXCPP"; then + if ${ac_cv_prog_CXXCPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 +$as_echo "$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +else + _lt_caught_CXX_error=yes +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +archive_cmds_need_lc_CXX=no +allow_undefined_flag_CXX= +always_export_symbols_CXX=no +archive_expsym_cmds_CXX= +compiler_needs_object_CXX=no +export_dynamic_flag_spec_CXX= +hardcode_direct_CXX=no +hardcode_direct_absolute_CXX=no +hardcode_libdir_flag_spec_CXX= +hardcode_libdir_separator_CXX= +hardcode_minus_L_CXX=no +hardcode_shlibpath_var_CXX=unsupported +hardcode_automatic_CXX=no +inherit_rpath_CXX=no +module_cmds_CXX= +module_expsym_cmds_CXX= +link_all_deplibs_CXX=unknown +old_archive_cmds_CXX=$old_archive_cmds +reload_flag_CXX=$reload_flag +reload_cmds_CXX=$reload_cmds +no_undefined_flag_CXX= +whole_archive_flag_spec_CXX= +enable_shared_with_static_runtimes_CXX=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +objext_CXX=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_caught_CXX_error"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + + # save warnings/boilerplate of simple test code + ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + + ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + compiler_CXX=$CC + func_cc_basename $compiler +cc_basename=$func_cc_basename_result + + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test yes = "$GXX"; then + lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' + else + lt_prog_compiler_no_builtin_flag_CXX= + fi + + if test yes = "$GXX"; then + # Set up default GNU C++ configuration + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return, which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD=$ac_prog + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test yes = "$with_gnu_ld"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD=$ac_dir/$ac_prog + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test yes = "$with_gnu_ld"; then + archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='$wl' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + whole_archive_flag_spec_CXX= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + ld_shlibs_CXX=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aix[4-9]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds_CXX='' + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + file_list_spec_CXX='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + hardcode_direct_CXX=no + hardcode_direct_absolute_CXX=no + ;; + esac + + if test yes = "$GXX"; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct_CXX=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L_CXX=yes + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_libdir_separator_CXX= + fi + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag=$shared_flag' $wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + export_dynamic_flag_spec_CXX='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + always_export_symbols_CXX=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + # The "-G" linker flag allows undefined symbols. + no_undefined_flag_CXX='-bernotok' + # Determine the default libpath from the value encoded in an empty + # executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" + + archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib' + allow_undefined_flag_CXX="-z nodefs" + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag_CXX=' $wl-bernotok' + allow_undefined_flag_CXX=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_CXX='$convenience' + fi + archive_cmds_need_lc_CXX=yes + archive_expsym_cmds_CXX='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared + # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_CXX=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + ld_shlibs_CXX=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec_CXX=' ' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=yes + file_list_spec_CXX='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' + enable_shared_with_static_runtimes_CXX=yes + # Don't use ranlib + old_postinstall_cmds_CXX='chmod 644 $oldlib' + postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_CXX='-L$libdir' + export_dynamic_flag_spec_CXX='$wl--export-all-symbols' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=no + enable_shared_with_static_runtimes_CXX=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs_CXX=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + + + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes + hardcode_shlibpath_var_CXX=unsupported + if test yes = "$lt_cv_ld_force_load"; then + whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec_CXX='' + fi + link_all_deplibs_CXX=yes + allow_undefined_flag_CXX=$_lt_dar_allow_undefined + case $cc_basename in + ifort*|nagfor*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test yes = "$_lt_dar_can_shared"; then + output_verbose_link_cmd=func_echo_all + archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + module_expsym_cmds_CXX="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + if test yes != "$lt_cv_apple_cc_single_mod"; then + archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" + archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" + fi + + else + ld_shlibs_CXX=no + fi + + ;; + + os2*) + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_minus_L_CXX=yes + allow_undefined_flag_CXX=unsupported + shrext_cmds=.dll + archive_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes_CXX=yes + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + ld_shlibs_CXX=no + ;; + + freebsd-elf*) + archive_cmds_need_lc_CXX=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + ld_shlibs_CXX=yes + ;; + + haiku*) + archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + link_all_deplibs_CXX=yes + ;; + + hpux9*) + hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='$wl-E' + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test no = "$with_gnu_ld"; then + hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' + hardcode_libdir_separator_CXX=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + export_dynamic_flag_spec_CXX='$wl-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + ;; + *) + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + interix[3-9]*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + export_dynamic_flag_spec_CXX='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds_CXX='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' + fi + fi + link_all_deplibs_CXX=yes + ;; + esac + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + hardcode_libdir_separator_CXX=: + inherit_rpath_CXX=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + archive_cmds_need_lc_CXX=no + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [1-5].* | *pgcpp\ [1-5].*) + prelink_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + old_archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + ;; + cxx*) + # Compaq C++ + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + if test yes = "$supports_anon_versioning"; then + archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' + hardcode_libdir_flag_spec_CXX='-R$libdir' + whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + compiler_needs_object_CXX=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + ld_shlibs_CXX=yes + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + hardcode_direct_absolute_CXX=yes + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' + export_dynamic_flag_spec_CXX='$wl-E' + whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + ld_shlibs_CXX=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx*) + case $host in + osf3*) + allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' + archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + ;; + *) + allow_undefined_flag_CXX=' -expect_unresolved \*' + archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ + $RM $lib.exp' + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + ;; + esac + + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes,no = "$GXX,$with_gnu_ld"; then + allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' + case $host in + osf3*) + archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + *) + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' + + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + archive_cmds_need_lc_CXX=yes + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_shlibpath_var_CXX=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' + ;; + esac + link_all_deplibs_CXX=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test yes,no = "$GXX,$with_gnu_ld"; then + no_undefined_flag_CXX=' $wl-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' + else + # g++ 2.7 appears to require '-G' NOT '-shared' on this + # platform. + archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' + fi + + hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir' + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag_CXX='$wl-z,text' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag_CXX='$wl-z,text' + allow_undefined_flag_CXX='$wl-z,nodefs' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='$wl-R,$libdir' + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + export_dynamic_flag_spec_CXX='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ + '"$old_archive_cmds_CXX" + reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ + '"$reload_cmds_CXX" + ;; + *) + archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } + test no = "$ld_shlibs_CXX" && can_build_shared=no + + GCC_CXX=$GXX + LD_CXX=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + # Dependencies to place before and after the object being linked: +predep_objects_CXX= +postdep_objects_CXX= +predeps_CXX= +postdeps_CXX= +compiler_lib_search_path_CXX= + +cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF + + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case $prev$p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test x-L = "$p" || + test x-R = "$p"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test no = "$pre_test_object_deps_done"; then + case $prev in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$compiler_lib_search_path_CXX"; then + compiler_lib_search_path_CXX=$prev$p + else + compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} $prev$p" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$postdeps_CXX"; then + postdeps_CXX=$prev$p + else + postdeps_CXX="${postdeps_CXX} $prev$p" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test no = "$pre_test_object_deps_done"; then + if test -z "$predep_objects_CXX"; then + predep_objects_CXX=$p + else + predep_objects_CXX="$predep_objects_CXX $p" + fi + else + if test -z "$postdep_objects_CXX"; then + postdep_objects_CXX=$p + else + postdep_objects_CXX="$postdep_objects_CXX $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling CXX test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +case $host_os in +interix[3-9]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + predep_objects_CXX= + postdep_objects_CXX= + postdeps_CXX= + ;; +esac + + +case " $postdeps_CXX " in +*" -lc "*) archive_cmds_need_lc_CXX=no ;; +esac + compiler_lib_search_dirs_CXX= +if test -n "${compiler_lib_search_path_CXX}"; then + compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | $SED -e 's! -L! !g' -e 's!^ !!'` +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lt_prog_compiler_wl_CXX= +lt_prog_compiler_pic_CXX= +lt_prog_compiler_static_CXX= + + + # C++ specific cases for pic, static, wl, etc. + if test yes = "$GXX"; then + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + fi + lt_prog_compiler_pic_CXX='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic_CXX='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + case $host_os in + os2*) + lt_prog_compiler_static_CXX='$wl-static' + ;; + esac + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_CXX='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + lt_prog_compiler_pic_CXX= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static_CXX= + ;; + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic_CXX=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + else + case $host_os in + aix[4-9]*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + else + lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + dgux*) + case $cc_basename in + ec++*) + lt_prog_compiler_pic_CXX='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='$wl-a ${wl}archive' + if test ia64 != "$host_cpu"; then + lt_prog_compiler_pic_CXX='+Z' + fi + ;; + aCC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='$wl-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_CXX='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + lt_prog_compiler_wl_CXX='--backend -Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64, which still supported -KPIC. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + lt_prog_compiler_static_CXX='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fpic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-qpic' + lt_prog_compiler_static_CXX='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + lt_prog_compiler_pic_CXX='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + lt_prog_compiler_wl_CXX='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + lt_prog_compiler_pic_CXX='-pic' + ;; + cxx*) + # Digital/Compaq C++ + lt_prog_compiler_wl_CXX='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + lt_prog_compiler_pic_CXX='-pic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + lcc*) + # Lucid + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + lt_prog_compiler_pic_CXX='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + lt_prog_compiler_can_build_shared_CXX=no + ;; + esac + fi + +case $host_os in + # For platforms that do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_CXX= + ;; + *) + lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } +lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } +if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works_CXX=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works_CXX=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } + +if test yes = "$lt_cv_prog_compiler_pic_works_CXX"; then + case $lt_prog_compiler_pic_CXX in + "" | " "*) ;; + *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; + esac +else + lt_prog_compiler_pic_CXX= + lt_prog_compiler_can_build_shared_CXX=no +fi + +fi + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works_CXX=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works_CXX=yes + fi + else + lt_cv_prog_compiler_static_works_CXX=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } + +if test yes = "$lt_cv_prog_compiler_static_works_CXX"; then + : +else + lt_prog_compiler_static_CXX= +fi + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + +hard_links=nottested +if test no = "$lt_cv_prog_compiler_c_o_CXX" && test no != "$need_locks"; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test no = "$hard_links"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + case $host_os in + aix[4-9]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + export_symbols_cmds_CXX=$ltdll_cmds + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs_CXX=no + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } +test no = "$ld_shlibs_CXX" && can_build_shared=no + +with_gnu_ld_CXX=$with_gnu_ld + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_CXX" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_CXX=yes + + if test yes,yes = "$GCC,$enable_shared"; then + case $archive_cmds_CXX in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_CXX + pic_flag=$lt_prog_compiler_pic_CXX + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_CXX + allow_undefined_flag_CXX= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc_CXX=no + else + lt_cv_archive_cmds_need_lc_CXX=yes + fi + allow_undefined_flag_CXX=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } + archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=.so +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + + + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='$libname$release$shared_ext$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test ia64 = "$host_cpu"; then + # AIX 5 supports IA64 + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a(lib.so.V)' + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='$libname$shared_ext' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec=$LIB + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 + fi + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +linux*android*) + version_type=none # Android doesn't support versioned libraries. + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + hardcode_libdir_flag_spec_CXX='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd* | bitrig*) + version_type=sunos + sys_lib_dlsearch_path_spec=/usr/lib + need_lib_prefix=no + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +os2*) + libname_spec='$name' + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test yes = "$with_gnu_ld"; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=sco + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test yes = "$with_gnu_ld"; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test no = "$dynamic_linker" && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test yes = "$GCC"; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec +fi + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec +fi + +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action_CXX= +if test -n "$hardcode_libdir_flag_spec_CXX" || + test -n "$runpath_var_CXX" || + test yes = "$hardcode_automatic_CXX"; then + + # We can hardcode non-existent directories. + if test no != "$hardcode_direct_CXX" && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" && + test no != "$hardcode_minus_L_CXX"; then + # Linking always hardcodes the temporary library directory. + hardcode_action_CXX=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_CXX=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_CXX=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 +$as_echo "$hardcode_action_CXX" >&6; } + +if test relink = "$hardcode_action_CXX" || + test yes = "$inherit_rpath_CXX"; then + # Fast installation is not supported + enable_fast_install=no +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test yes != "$_lt_caught_CXX_error" + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + + + + + + + + + + + + + + ac_config_commands="$ac_config_commands libtool" + + + + +# Only expand once: + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Extract the first word of "mkdir", so it can be a program name with args. +set dummy mkdir; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_MKDIR+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MKDIR in + [\\/]* | ?:[\\/]*) + ac_cv_path_MKDIR="$MKDIR" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_MKDIR="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +MKDIR=$ac_cv_path_MKDIR +if test -n "$MKDIR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR" >&5 +$as_echo "$MKDIR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +# Documentation. +# Check whether --enable-documentation was given. +if test "${enable_documentation+set}" = set; then : + enableval=$enable_documentation; +else + enable_documentation=auto +fi + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}doxygen", so it can be a program name with args. +set dummy ${ac_tool_prefix}doxygen; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_DOXYGEN+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $DOXYGEN in + [\\/]* | ?:[\\/]*) + ac_cv_path_DOXYGEN="$DOXYGEN" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_DOXYGEN="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +DOXYGEN=$ac_cv_path_DOXYGEN +if test -n "$DOXYGEN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DOXYGEN" >&5 +$as_echo "$DOXYGEN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_DOXYGEN"; then + ac_pt_DOXYGEN=$DOXYGEN + # Extract the first word of "doxygen", so it can be a program name with args. +set dummy doxygen; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_DOXYGEN+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_DOXYGEN in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_DOXYGEN="$ac_pt_DOXYGEN" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_DOXYGEN="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_DOXYGEN=$ac_cv_path_ac_pt_DOXYGEN +if test -n "$ac_pt_DOXYGEN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DOXYGEN" >&5 +$as_echo "$ac_pt_DOXYGEN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_DOXYGEN" = x; then + DOXYGEN="nodoxygen" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DOXYGEN=$ac_pt_DOXYGEN + fi +else + DOXYGEN="$ac_cv_path_DOXYGEN" +fi + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dot", so it can be a program name with args. +set dummy ${ac_tool_prefix}dot; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_HAVE_DOT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$HAVE_DOT"; then + ac_cv_prog_HAVE_DOT="$HAVE_DOT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in NO +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_HAVE_DOT="${ac_tool_prefix}dot" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +HAVE_DOT=$ac_cv_prog_HAVE_DOT +if test -n "$HAVE_DOT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $HAVE_DOT" >&5 +$as_echo "$HAVE_DOT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_HAVE_DOT"; then + ac_ct_HAVE_DOT=$HAVE_DOT + # Extract the first word of "dot", so it can be a program name with args. +set dummy dot; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_HAVE_DOT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_HAVE_DOT"; then + ac_cv_prog_ac_ct_HAVE_DOT="$ac_ct_HAVE_DOT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in NO +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_HAVE_DOT="dot" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_HAVE_DOT=$ac_cv_prog_ac_ct_HAVE_DOT +if test -n "$ac_ct_HAVE_DOT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_HAVE_DOT" >&5 +$as_echo "$ac_ct_HAVE_DOT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_HAVE_DOT" = x; then + HAVE_DOT="YES" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + HAVE_DOT=$ac_ct_HAVE_DOT + fi +else + HAVE_DOT="$ac_cv_prog_HAVE_DOT" +fi + +if test "$enable_documentation" = "yes" && test "$DOXYGEN" = "nodoxygen"; then : + as_fn_error $? "could not find tools necessary to build documentation" "$LINENO" 5 +fi + if test "$enable_documentation" != "no" -a "$DOXYGEN" != "nodoxygen"; then + BUILD_REFERENCE_TRUE= + BUILD_REFERENCE_FALSE='#' +else + BUILD_REFERENCE_TRUE='#' + BUILD_REFERENCE_FALSE= +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } + # Check whether --enable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then : + enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 +$as_echo "$USE_MAINTAINER_MODE" >&6; } + if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + + +# See if we want stricter compiler warnings. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking maintainer mode" >&5 +$as_echo_n "checking maintainer mode... " >&6; } +# Check whether --enable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then : + enableval=$enable_maintainer_mode; +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${enable_maintainer_mode}" >&5 +$as_echo "${enable_maintainer_mode}" >&6; } + +# See if we want runtime debug checking. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking audit" >&5 +$as_echo_n "checking audit... " >&6; } +# Check whether --enable-audit was given. +if test "${enable_audit+set}" = set; then : + enableval=$enable_audit; +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${enable_audit}" >&5 +$as_echo "${enable_audit}" >&6; } + +# See if we want "suggestions," such as "this class could be final." +# (The suggestions are often useful, but can also easily be wrong.) +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking suggest" >&5 +$as_echo_n "checking suggest... " >&6; } +# Check whether --enable-suggest was given. +if test "${enable_suggest+set}" = set; then : + enableval=$enable_suggest; +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${enable_suggest}" >&5 +$as_echo "${enable_suggest}" >&6; } + + +# Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; +fi + +if test "${shared}" = "yes" ; then : + CPPFLAGS="$CPPFLAGS -DPQXX_SHARED" +fi + + +# Add options to compiler command line, if compiler accepts them. +add_compiler_opts_if_ok() { + for option in $* + do + ACO_SAVE_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $option" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts $option" >&5 +$as_echo_n "checking whether $CXX accepts $option... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + has_option=yes +else + has_option=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $has_option" >&5 +$as_echo "$has_option" >&6; } + if test "$has_option" = "no" ; then : + CXXFLAGS="$ACO_SAVE_CXXFLAGS" +fi + done +} + + +# Add options to compiler command line, unconditionally. +add_compiler_opts() { + CXXFLAGS="$CXXFLAGS $*" +} + + +# It's tempting to use Autoconf Archive's AX_CXX_COMPILE_STDCXX_17 for this, +# but it's 2022 and the C++20 equivalent isn't quite ready for use. +# Seems simpler and more reliable for the user to arrange for the desired +# language versions by setting the appropriate option for their compiler. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sufficient C++ language/library level" >&5 +$as_echo_n "checking for sufficient C++ language/library level... " >&6; } +sufficient_cxx=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #if __cplusplus < 201611L + #error "Need C++17 or better." + #endif + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + sufficient_cxx=yes +else + sufficient_cxx=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sufficient_cxx" >&5 +$as_echo "$sufficient_cxx" >&6; } +if test "$sufficient_cxx" != "yes" +then + as_fn_error $? "This libpqxx version needs at least C++17." "$LINENO" 5 +fi + + +# Let's try to get the compiler to be helpful. +# +# (Omit options -Weffc++ and -Wabi because they currently yield too many +# warnings in gcc's standard headers; omit -Wunreachable-code because it isn't +# always right) +if test "$GCC" = "yes" +then + # In maintainer mode, enable all the warning options we can. + if test "$enable_maintainer_mode" = "yes" + then + # "Eternal" (FLW) g++ options. These have been around for + # ages, and both g++ and clang++ support them. Don't bother + # checking for support; just add them to the compiler options. + add_compiler_opts \ + -fstrict-enums \ + -Werror \ + -Wall \ + -pedantic \ + -Wcast-align \ + -Wcast-qual \ + -Wconversion \ + -Wctor-dtor-privacy \ + -Wendif-labels \ + -Wextra \ + -Wfloat-equal \ + -Wformat=2 \ + -Wformat-security \ + -Wmissing-include-dirs \ + -Wno-div-by-zero \ + -Wnon-virtual-dtor \ + -Wold-style-cast \ + -Woverlength-strings \ + -Woverloaded-virtual \ + -Wpointer-arith \ + -Wredundant-decls \ + -Wshadow \ + -Wsign-promo \ + -Wundef \ + -Wunused \ + -Wwrite-strings + + # "Iffy" g++ options. Some reasonably current g++-like + # compilers may not support these. + add_compiler_opts_if_ok \ + -fnothrow-opt \ + -Wattribute-alias=2 \ + -Wextra-semi \ + -Wlogical-op \ + -Wmismatched-tags \ + -Wnoexcept \ + -Wredundant-tags \ + -Wrestrict \ + -Wstringop-overflow \ + -Wzero-as-null-pointer-constant \ + -Warray-bounds=2 \ + -Wduplicated-branches \ + -Wduplicated-cond \ + -Wsuggest-attribute=noreturn \ + -Wsuggest-override \ + -Wtrampolines + fi + + # In "audit," enable all runtime checks we can. + if test "$enable_audit" = "yes" + then + add_compiler_opts_if_ok \ + -D_FORTIFY_SOURCE=2 \ + -fsanitize=address \ + -fsanitize-address-use-after-scope \ + -fsanitize=alignment \ + -fsanitize=bool \ + -fsanitize=bounds \ + -fsanitize=bounds-strict \ + -fsanitize=builtin \ + -fsanitize=enum \ + -fsanitize=float-cast-overflow \ + -fsanitize=float-divide-by-zero \ + -fsanitize=integer-divide-by-zero \ + -fsanitize=leak \ + -fsanitize=nonnull-attribute \ + -fsanitize=null \ + -fsanitize=object-size \ + -fsanitize=pointer-compare \ + -fsanitize=pointer-overflow \ + -fsanitize=pointer-subtract \ + -fsanitize=return \ + -fsanitize=returns-nonnull-attribute \ + -fsanitize=shift \ + -fsanitize=shift-base \ + -fsanitize=shift-exponent \ + -fsanitize=signed-integer-overflow \ + -fsanitize=undefined \ + -fsanitize=unreachable \ + -fsanitize=vla-bound \ + -fsanitize=vptr \ + -fstack-protector-all + fi + + # In "suggest" mode, enable a bunch of code suggestions. + if test "$enable_suggest" = "yes" + then + add_compiler_opts_if_ok \ + -Wsuggest-attribute=cold \ + -Wsuggest-attribute=const \ + -Wsuggest-attribute=malloc \ + -Wsuggest-attribute=pure \ + -Wsuggest-final-types \ + -Wsuggest-final-methods + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking g++ visibility attribute" >&5 +$as_echo_n "checking g++ visibility attribute... " >&6; } +gcc_visibility=yes +SAVE_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$CXXFLAGS -Werror" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for gcc-style "visibility" attribute. + +struct __attribute__((visibility("hidden"))) D + +{ + + D() {} + + int f() { return 0; } + +}; + + + +int main() + +{ + + D d; + + return d.f(); + +} + + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_GCC_VISIBILITY 1" >>confdefs.h + +else + gcc_visibility=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_visibility" >&5 +$as_echo "$gcc_visibility" >&6; } +CXXFLAGS="$SAVE_CXXFLAGS" +if test "$gcc_visibility" = "yes" +then + # Make internal definitions accessible only to the library itself. + # Only definitions marked PQXX_LIBEXPORT will be accessible. + add_compiler_opts -fvisibility=hidden + add_compiler_opts -fvisibility-inlines-hidden +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking g++ pure attribute" >&5 +$as_echo_n "checking g++ pure attribute... " >&6; } +gcc_pure=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for gcc-style "pure" attribute. + +int __attribute__((pure)) f() + +{ + + return 0; + +} + + + +int main() + +{ + + return f(); + +} + + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_GCC_PURE 1" >>confdefs.h + +else + gcc_pure=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_pure" >&5 +$as_echo "$gcc_pure" >&6; } + +fi # End of gcc-specific part. + + +# Check for __cxa_demangle. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking __cxa_demangle" >&5 +$as_echo_n "checking __cxa_demangle... " >&6; } +cxa_demangle=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for cross-vendor C++ ABI's __cxa_demangle function. + +#include + +#include + +#include + +#include + + + +#include + + + +int main() + +{ + + int status = 0; + + char *name = + + abi::__cxa_demangle(typeid(10).name(), nullptr, nullptr, &status); + + if (status != 0) + + throw std::runtime_error("Demangle failed!"); + + int result = std::strcmp(name, "int"); + + std::free(name); + + return result; + +} + + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_CXA_DEMANGLE 1" >>confdefs.h + +else + cxa_demangle=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cxa_demangle" >&5 +$as_echo "$cxa_demangle" >&6; } + + +# Check for sufficient Concepts support, introduced with C++20. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking concepts" >&5 +$as_echo_n "checking concepts... " >&6; } +concepts=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for concepts support. Not just the language feature; also headers. + +#include + +#include + +#include + + + + + +template + +concept Foo = std::ranges::input_range; + + + + + +template auto foo(F const &r) + +{ + + return std::distance(std::begin(r), std::end(r)); + +} + + + + + +int main() + +{ + + std::vector const v{1, 2, 3}; + + std::cout << foo(v) << '\n'; + +} + + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_CONCEPTS 1" >>confdefs.h + +else + concepts=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $concepts" >&5 +$as_echo "$concepts" >&6; } + + +# Check for C++20 std::span. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking std::span" >&5 +$as_echo_n "checking std::span... " >&6; } +span=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for std::span. + +#include + + + +int main(int argc, char **argv) + +{ + + std::span args{argv, static_cast(argc)}; + + return static_cast(std::size(args) - 1u); + +} + + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_SPAN 1" >>confdefs.h + +else + span=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $span" >&5 +$as_echo "$span" >&6; } + + +# Check for multidimensional subscript operator support. Proposed for C++23. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for multidimensional subscript operator support" >&5 +$as_echo_n "checking for multidimensional subscript operator support... " >&6; } +multidim_subscript=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for multidimensional subscript operator support. + +// Proposed for C++23: P2128R6. + +struct table + +{ + + int width = 100; + + + + int operator[](int x, int y) const { return x + width * y; } + +}; + + + + + +int main() + +{ + + return table{}[0, 0]; + +} + + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_MULTIDIMENSIONAL_SUBSCRIPT 1" >>confdefs.h + +else + multidim_subscript=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $multidim_subscript" >&5 +$as_echo "$multidim_subscript" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for strerror_r()" >&5 +$as_echo_n "checking for strerror_r()... " >&6; } +strerror_r=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Check for strerror_r. + +// It can be either the POSIX version (which returns int) or the GNU version + +// (which returns char *). + + + +#include + +#include + + + +int main() + +{ + + char buffer[200]; + + auto res{strerror_r(1, buffer, 200)}; + + // Sidestep type differences. We don't really care what the value is. + + return not not res; + +} + + +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_STRERROR_R 1" >>confdefs.h + +else + strerror_r=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $strerror_r" >&5 +$as_echo "$strerror_r" >&6; } + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for strerror_s()" >&5 +$as_echo_n "checking for strerror_s()... " >&6; } +strerror_s=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for strerror_s, as defined in Windows and C11. + +// Presumably this'll be part of the C++ standard some day. + + + +#include + + + +int main() + +{ + + using namespace std; + + char buf[200]; + + return strerror_s(buf, 200, 1); + +} + + +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_STRERROR_S 1" >>confdefs.h + +else + strerror_s=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $strerror_s" >&5 +$as_echo "$strerror_s" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::chrono::year_month_day etc" >&5 +$as_echo_n "checking for std::chrono::year_month_day etc... " >&6; } +year_month_day=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for std::chrono::year_month_day etc. + +#include + + + +int main() + +{ + + return int(std::chrono::year{1}); + +} + + +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_YEAR_MONTH_DAY 1" >>confdefs.h + +else + year_month_day=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $year_month_day" >&5 +$as_echo "$year_month_day" >&6; } + + +# Check for [[likely]] and [[unlikely]] attributes. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking attributes \"likely\" and \"unlikely\"." >&5 +$as_echo_n "checking attributes \"likely\" and \"unlikely\".... " >&6; } +likely=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for C++20 [[likely]] and [[unlikely]] attributes. + + + +int main(int argc, char **) + +{ + +#if __cplusplus < 202002L + + deliberately_fail(because, older, C++, standard); + +#endif + + + + int x = 0; + + if (argc == 1) [[likely]] + + x = 0; + + else + + x = 1; + + return x; + +} + + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_LIKELY 1" >>confdefs.h + +else + likely=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $likely" >&5 +$as_echo "$likely" >&6; } + + +# It's mid-2019, and gcc's charconv supports integers but not yet floats. +# So for now, we test for int and float conversion... separately. +# +# It's worse for older clang versions, which compile the integer conversions +# but then fail at link time because of a missing symbol "__muloti4" with the +# "long long" version. I couldn't resolve that symbol by adding -lm either. +# So don't just compile these tests; link them as well. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++17 charconv integer conversion" >&5 +$as_echo_n "checking for C++17 charconv integer conversion... " >&6; } +have_charconv_int=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for std::to_string/std::from_string for integral types. + +#include + +#include + + + +int main() + +{ + + char z[100]; + + auto rt = std::to_chars(std::begin(z), std::end(z), 9ULL); + + if (rt.ec != std::errc{}) + + return 1; + + unsigned long long n; + + auto rf = std::from_chars(std::cbegin(z), std::cend(z), n); + + if (rf.ec != std::errc{}) + + return 2; + + return (n == 9ULL) ? 0 : 1; + +} + + +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_CHARCONV_INT 1" >>confdefs.h + +else + have_charconv_int=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_charconv_int" >&5 +$as_echo "$have_charconv_int" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++17 charconv floating-point conversion" >&5 +$as_echo_n "checking for C++17 charconv floating-point conversion... " >&6; } +have_charconv_float=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for std::to_string/std::from_string for floating-point types. + +#include + +#include + + + +int main() + +{ + + char z[100]; + + auto rt = std::to_chars(std::begin(z), std::end(z), 3.14159L); + + if (rt.ec != std::errc{}) + + return 1; + + long double n; + + auto rf = std::from_chars(std::cbegin(z), std::cend(z), n); + + if (rf.ec != std::errc{}) + + return 2; + + return (n > 3 and n < 4) ? 0 : 1; + +} + + +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_CHARCONV_FLOAT 1" >>confdefs.h + +else + have_charconv_float=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_charconv_float" >&5 +$as_echo "$have_charconv_float" >&6; } + +# As per #262, clang with libcxxrt does not support thread_local on non-POD +# objects. Luckily we can live without those, it's just less efficient. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for full thread_local support" >&5 +$as_echo_n "checking for full thread_local support... " >&6; } +have_thread_local=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for std::to_string/std::from_string for floating-point types. + +#include + +#include + + + +int main(int argc, char **) + +{ + +#if defined(__MINGW32__) && defined(__GNUC__) + +# if __GNUC__ < 11 || ((__GNUC__ == 11) && (__GNU_MINOR__ == 0)) + +# error "On MinGW before gcc 11.1, thread_local breaks at run time." + +# endif + +#endif + + thread_local std::stringstream s; + + s << argc; + + std::cout << s.str(); + +} + + +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_THREAD_LOCAL 1" >>confdefs.h + +else + have_thread_local=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_thread_local" >&5 +$as_echo "$have_thread_local" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::this_thread::sleep_for" >&5 +$as_echo_n "checking for std::this_thread::sleep_for... " >&6; } +have_sleep_for=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for std::this_thread::sleep_for(). + +/* For some reason MinGW's header seems to be broken. + + * + + * But it gets worse. It looks as if we can include without problems + + * in this configuration test. Why does it break when MinGW users try to build + + * the library, but succeed when we try it here? + + * + + * To try and get close to the situation in the library code itself, we try + + * including some standard headers that we don't strictly need here. + + */ + + + +#if __has_include() + +# include + +#endif + + + +#include + +#include + +#include + +#include + +#include + + + +#include + +#include + + + +int main() + +{ + + std::this_thread::sleep_for(std::chrono::microseconds{10u}); + +} + + +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_SLEEP_FOR 1" >>confdefs.h + +else + have_sleep_for=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_sleep_for" >&5 +$as_echo "$have_sleep_for" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::cmp_greater, std::cmp_less_equal, etc" >&5 +$as_echo_n "checking for std::cmp_greater, std::cmp_less_equal, etc... " >&6; } +have_cmp=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for C++20 std::cmp_greater etc. support. + +#include + + + + + +int main() + +{ + + return std::cmp_greater(-1, 2u) && std::cmp_less_equal(3, 0); + +} + + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_CMP 1" >>confdefs.h + +else + have_cmp=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_cmp" >&5 +$as_echo "$have_cmp" >&6; } + + +# Doing my own check for poll(). There's one built into autoconf-archive, but +# it produces warnings in C++ (about unnecessarily using "struct", and using 0 +# as a null pointer constant). In maintainer mode, those warnings turn into +# errors. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for poll()" >&5 +$as_echo_n "checking for poll()... " >&6; } +ax_cv_have_poll=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for poll(). + +#include + + + +int main() + +{ + + return poll(nullptr, 0, 0); + +} + + +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_POLL 1" >>confdefs.h + +else + ax_cv_have_poll=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_poll" >&5 +$as_echo "$ax_cv_have_poll" >&6; } + +if test "$ax_cv_have_poll" != "yes" +then +# No poll(); we'll fall back to select(). + +# Some systems keep select() in a separate library which is not linked by +# default. See if we need one of those. +socklibok=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing select" >&5 +$as_echo_n "checking for library containing select... " >&6; } +if ${ac_cv_search_select+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char select (); +int +main () +{ +return select (); + ; + return 0; +} +_ACEOF +for ac_lib in '' socket nsl ws2_32 wsock32 winsock; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_cxx_try_link "$LINENO"; then : + ac_cv_search_select=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_select+:} false; then : + break +fi +done +if ${ac_cv_search_select+:} false; then : + +else + ac_cv_search_select=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_select" >&5 +$as_echo "$ac_cv_search_select" >&6; } +ac_res=$ac_cv_search_select +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + socklibok=yes +fi + + +# Microsoft proprietary libraries do not work with code that is generated with +# autoconf's SEARCH_LIBS macro, so we need to check manually and just use the +# first socket library available. +# We only do this if select() is not available by other means, to avoid picking +# up an unnecessary Windows compatibility library on a non-Windows system. +for l in ws2_32 wsock32 winsock +do + if test "${socklibok}" != "yes" + then + as_ac_Lib=`$as_echo "ac_cv_lib_$l''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$l" >&5 +$as_echo_n "checking for main in -l$l... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$l $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + LIBS="$LIBS -l$l";socklibok=yes +fi + + fi +done + +if test "${socklibok}" != "yes" +then + as_fn_error $? " +Could not figure out how to link a simple sockets-based program. Please read +the config.log file for more clues as to why this failed. +" "$LINENO" 5 +fi + +fi # No poll() + + +# Find PostgreSQL includes and libraries +# Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +for ac_prog in pg_config +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PG_CONFIG="$PG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PG_CONFIG=$ac_cv_path_PG_CONFIG +if test -n "$PG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PG_CONFIG" >&5 +$as_echo "$PG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$PG_CONFIG" && break +done + + + +# Check whether --with-postgres-include was given. +if test "${with_postgres_include+set}" = set; then : + withval=$with_postgres_include; if test "x$with_postgres_include" = "xyes"; then : + with_postgres_include="" +fi +fi + + +if test -n "$with_postgres_include" +then + POSTGRES_INCLUDE="-I$with_postgres_include" +else + if test -x "$PKG_CONFIG" || test -x "$PG_CONFIG" + then + # We should prefer pkg-config over pg_config, but there seems to be a + # problem in pkg-config 1.6.3. Until that's been resolved (#291), go + # with pg_config if we can. + if test -x "$PG_CONFIG" + then + # From pg_config we can either get the C compiler options used to + # compile postgres, which isn't quite what we want; or we can get + # the headers directory, without the full option. That's something + # we can work with. The compiler must support the "-I" option for + # that, but both scripts assume that anyway. + POSTGRES_INCLUDE="-I$($PG_CONFIG --includedir)" + else + # From pkg-config we can get the compiler options to extend the + # include path. We use that. + POSTGRES_INCLUDE=$($PKG_CONFIG libpq --cflags-only-I) + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: finding PostgreSQL headers using $POSTGRES_INCLUDE" >&5 +$as_echo "$as_me: finding PostgreSQL headers using $POSTGRES_INCLUDE" >&6;} + else + POSTGRES_INCLUDE="" + + # We have nothing to tell us where the libpq headers are. That's fine + # if the compiler can find it, but if not, fail here. + +ac_fn_cxx_check_header_mongrel "$LINENO" "libpq-fe.h" "ac_cv_header_libpq_fe_h" "$ac_includes_default" +if test "x$ac_cv_header_libpq_fe_h" = xyes; then : + +else + as_fn_error $? " +Can't find the main PostgreSQL client header, libpq-fe.h. Make sure that it +is installed, and either use the --with-postgres-include option or install +pkg-config. +" "$LINENO" 5 +fi + + + fi +fi + + +# Add the compiler option so we can compile configure tests which rely on the +# libpq headers. +CPPFLAGS="$CPPFLAGS $POSTGRES_INCLUDE" + + + +# Check whether --with-postgres-lib was given. +if test "${with_postgres_lib+set}" = set; then : + withval=$with_postgres_lib; if test "x$with_postgres_lib" = "xyes"; then : + with_postgres_lib="" +fi +fi + + +# If no --with-postgres-lib was given, and we have pkg-config, use that. +if test -z "$with_postgres_lib" -a -x "$PKG_CONFIG"; then : + with_postgres_lib=$($PKG_CONFIG libpq --libs-only-L | sed 's/^-L//') +fi + +# pg_config is deprecated, but for some users it may still provide the only +# right answer. For instance, `pkg-config` may not know where `libpq` is +# installed. +if test -z "$with_postgres_lib" -a -x "$PG_CONFIG"; then : + with_postgres_lib=$($PG_CONFIG --libdir) +fi + +if test -n "$with_postgres_lib"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: using PostgreSQL libraries at $with_postgres_lib" >&5 +$as_echo "$as_me: using PostgreSQL libraries at $with_postgres_lib" >&6;} +else + { $as_echo "$as_me:${as_lineno-$LINENO}: using PostgreSQL libraries in default location" >&5 +$as_echo "$as_me: using PostgreSQL libraries in default location" >&6;} +fi + + + + +ac_fn_cxx_check_header_mongrel "$LINENO" "libpq-fe.h" "ac_cv_header_libpq_fe_h" "$ac_includes_default" +if test "x$ac_cv_header_libpq_fe_h" = xyes; then : + +else + as_fn_error $? " +Can't find the main PostgreSQL client header, libpq-fe.h. Are you sure the +libpq headers are installed correctly, and that we've got the right path? +" "$LINENO" 5 +fi + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ability to compile source files using libpq" >&5 +$as_echo_n "checking for ability to compile source files using libpq... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +PQexec(nullptr,"") + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + as_fn_error $? " +Could not compile a call to a basic libpq function. There must be something +seriously wrong with the headers that \"pg_config --includedir\" or \"pkg-config +libpq --cflags\" pointed to; the contents of config.log may give you a clue +about the nature of the failure. +Source including the libpq header libpq-fe.h can be compiled, but a call to the +most basic libpq function PQexec() failed to compile successfully. This is the +litmus test for a working libpq. +" "$LINENO" 5 +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + + +if test "x${with_postgres_lib}" = "x"; then + with_postgres_libpath="" +else + with_postgres_libpath="-L${with_postgres_lib}" +fi +LDFLAGS="$LDFLAGS ${with_postgres_libpath}" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PQexec in -lpq" >&5 +$as_echo_n "checking for PQexec in -lpq... " >&6; } +if ${ac_cv_lib_pq_PQexec+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpq ${with_postgres_libpath} $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char PQexec (); +int +main () +{ +return PQexec (); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + ac_cv_lib_pq_PQexec=yes +else + ac_cv_lib_pq_PQexec=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pq_PQexec" >&5 +$as_echo "$ac_cv_lib_pq_PQexec" >&6; } +if test "x$ac_cv_lib_pq_PQexec" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPQ 1 +_ACEOF + + LIBS="-lpq $LIBS" + +else + as_fn_error $? " +Did not find the PQexec() function in libpq. This is the litmus test for a +working libpq installation. + +A source file using the PQexec() function did compile without problems, and the +libpq library is available for linking, but for some reason a call to PQexec() +failed to link properly to the libpq library. This may be because the libpq +library file is damaged, or in some incorrect format, or if your libpq is much +more recent than libpqxx version $PQXX_ABI, perhaps libpq has undergone a +radical ABI change. + +The last parts of config.log may give you a clue as to what really went wrong, +but be warned that this is no easy reading. Look for the last error message +occurring in the file. +" "$LINENO" 5 +fi + + + +# PQencryptPasswordConn was added in postgres 10. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PQencryptPasswordConn" >&5 +$as_echo_n "checking for PQencryptPasswordConn... " >&6; } +have_pqencryptpasswordconn=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + extern PGconn *conn; + PQencryptPasswordConn( + conn, "pwd", "user", "scram-sha-256") + + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_PQENCRYPTPASSWORDCONN 1" >>confdefs.h + +else + have_pqencryptpasswordconn=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_pqencryptpasswordconn" >&5 +$as_echo "$have_pqencryptpasswordconn" >&6; } + + +# "Pipeline mode" was introduced in libpq 14. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libpq pipeline mode" >&5 +$as_echo_n "checking for libpq pipeline mode... " >&6; } +have_pq_pipeline=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + extern PGconn *conn; + PQenterPipelineMode(conn) + + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_PQ_PIPELINE 1" >>confdefs.h + +else + have_pq_pipeline=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_pq_pipeline" >&5 +$as_echo "$have_pq_pipeline" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for usable std::filesystem::path" >&5 +$as_echo_n "checking for usable std::filesystem::path... " >&6; } +have_path=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Check for working std::filesystem support. + +#include + + + + + +int main() + +{ + + // Apparently some versions of MinGW lack this comparison operator. + + return std::filesystem::path{} != std::filesystem::path{}; + +} + + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_PATH 1" >>confdefs.h + +else + have_path=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_path" >&5 +$as_echo "$have_path" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need a link option for support" >&5 +$as_echo_n "checking whether we need a link option for support... " >&6; } +LIBS_SAVE="$LIBS" +found_fslib=no +for l in '' '-lstdc++fs' '-lc++fs' +do + if test "$found_fslib" != "yes" + then + LIBS="$LIBS $l" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Check whether we need to link to the stdc++fs library. + +// + +// We assume that the presence of the header means that we have + +// support for the basics of std::filesystem. This check will succeed if + +// either there is no header, or there is one and it works without + +// any special options. If the link fails, we assume that -lstdc++fs will fix + +// it for us. + + + +#include + + + +#if __has_include() + +# include + +#endif + + + + + +int main() + +{ + +#if __has_include() + + std::cout << std::filesystem::path{"foo.bar"}.c_str() << '\n'; + +#endif + +} + + +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + found_fslib=yes +else + LIBS="$LIBS_SAVE" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test "$found_fslib" = "yes" + then + result_msg="$l" + # (And keep our current value of $LIBS.) + fi + fi +done + +if test "$found_fslib" != "yes" +then + as_fn_error $? " +There seems to be support, but I could not figure out now to make +it work. You'll have to add set your own build options for this. + " "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $result_msg" >&5 +$as_echo "$result_msg" >&6; } + + +# Remove redundant occurrances of -lpq +LIBS=$(echo "$LIBS" | sed -e 's/-lpq * -lpq\>/-lpq/g') + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that type of libpq's Oid is as expected" >&5 +$as_echo_n "checking that type of libpq's Oid is as expected... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + #include"${srcdir}/include/pqxx/internal/libpq-forward.hxx" + extern void f(pqxx::oid&); + +int +main () +{ +Oid o;f(o) + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + as_fn_error $? " +The Oid typedef in libpq has changed. Please notify the libpqxx authors of the +change! +" "$LINENO" 5 +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + + +ac_config_files="$ac_config_files Makefile config/Makefile doc/Makefile doc/Doxyfile src/Makefile test/Makefile tools/Makefile include/Makefile include/pqxx/Makefile libpqxx.pc" + + + +ac_config_commands="$ac_config_commands configitems" + + +ac_config_files="$ac_config_files compile_flags" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_REFERENCE_TRUE}" && test -z "${BUILD_REFERENCE_FALSE}"; then + as_fn_error $? "conditional \"BUILD_REFERENCE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by libpqxx $as_me 7.7.3, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +libpqxx config.status 7.7.3 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" + + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' +macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' +macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' +enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' +pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' +enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' +shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' +SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' +ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' +PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' +host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' +host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' +host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' +build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' +build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' +build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' +SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' +Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' +GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' +EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' +FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' +LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' +NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' +LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' +max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' +ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' +exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' +lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' +lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' +lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' +lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' +lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' +reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' +reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' +OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' +deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' +file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' +file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' +want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' +DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' +sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' +AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' +AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' +archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' +STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' +RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' +old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' +old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' +lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' +CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' +CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' +compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' +GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' +lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' +nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' +lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' +lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' +objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' +MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' +need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' +MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' +DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' +NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' +LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' +OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' +OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' +libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' +shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' +extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' +compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' +module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' +with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' +no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' +hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' +hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' +inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' +link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' +always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' +exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' +include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' +prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' +postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' +file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' +variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' +need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' +need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' +version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' +runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' +libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' +library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' +soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' +install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' +postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' +postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' +finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' +hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' +sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' +configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' +configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' +hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' +enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' +old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' +striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' +predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' +postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' +predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' +postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' +LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' +reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' +reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' +GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' +inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' +link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' +always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' +exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' +predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' +postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' +predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' +postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' + +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in SHELL \ +ECHO \ +PATH_SEPARATOR \ +SED \ +GREP \ +EGREP \ +FGREP \ +LD \ +NM \ +LN_S \ +lt_SP2NL \ +lt_NL2SP \ +reload_flag \ +OBJDUMP \ +deplibs_check_method \ +file_magic_cmd \ +file_magic_glob \ +want_nocaseglob \ +DLLTOOL \ +sharedlib_from_linklib_cmd \ +AR \ +AR_FLAGS \ +archiver_list_spec \ +STRIP \ +RANLIB \ +CC \ +CFLAGS \ +compiler \ +lt_cv_sys_global_symbol_pipe \ +lt_cv_sys_global_symbol_to_cdecl \ +lt_cv_sys_global_symbol_to_import \ +lt_cv_sys_global_symbol_to_c_name_address \ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ +lt_cv_nm_interface \ +nm_file_list_spec \ +lt_cv_truncate_bin \ +lt_prog_compiler_no_builtin_flag \ +lt_prog_compiler_pic \ +lt_prog_compiler_wl \ +lt_prog_compiler_static \ +lt_cv_prog_compiler_c_o \ +need_locks \ +MANIFEST_TOOL \ +DSYMUTIL \ +NMEDIT \ +LIPO \ +OTOOL \ +OTOOL64 \ +shrext_cmds \ +export_dynamic_flag_spec \ +whole_archive_flag_spec \ +compiler_needs_object \ +with_gnu_ld \ +allow_undefined_flag \ +no_undefined_flag \ +hardcode_libdir_flag_spec \ +hardcode_libdir_separator \ +exclude_expsyms \ +include_expsyms \ +file_list_spec \ +variables_saved_for_relink \ +libname_spec \ +library_names_spec \ +soname_spec \ +install_override_mode \ +finish_eval \ +old_striplib \ +striplib \ +compiler_lib_search_dirs \ +predep_objects \ +postdep_objects \ +predeps \ +postdeps \ +compiler_lib_search_path \ +LD_CXX \ +reload_flag_CXX \ +compiler_CXX \ +lt_prog_compiler_no_builtin_flag_CXX \ +lt_prog_compiler_pic_CXX \ +lt_prog_compiler_wl_CXX \ +lt_prog_compiler_static_CXX \ +lt_cv_prog_compiler_c_o_CXX \ +export_dynamic_flag_spec_CXX \ +whole_archive_flag_spec_CXX \ +compiler_needs_object_CXX \ +with_gnu_ld_CXX \ +allow_undefined_flag_CXX \ +no_undefined_flag_CXX \ +hardcode_libdir_flag_spec_CXX \ +hardcode_libdir_separator_CXX \ +exclude_expsyms_CXX \ +include_expsyms_CXX \ +file_list_spec_CXX \ +compiler_lib_search_dirs_CXX \ +predep_objects_CXX \ +postdep_objects_CXX \ +predeps_CXX \ +postdeps_CXX \ +compiler_lib_search_path_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds \ +old_postinstall_cmds \ +old_postuninstall_cmds \ +old_archive_cmds \ +extract_expsyms_cmds \ +old_archive_from_new_cmds \ +old_archive_from_expsyms_cmds \ +archive_cmds \ +archive_expsym_cmds \ +module_cmds \ +module_expsym_cmds \ +export_symbols_cmds \ +prelink_cmds \ +postlink_cmds \ +postinstall_cmds \ +postuninstall_cmds \ +finish_cmds \ +sys_lib_search_path_spec \ +configure_time_dlsearch_path \ +configure_time_lt_sys_library_path \ +reload_cmds_CXX \ +old_archive_cmds_CXX \ +old_archive_from_new_cmds_CXX \ +old_archive_from_expsyms_cmds_CXX \ +archive_cmds_CXX \ +archive_expsym_cmds_CXX \ +module_cmds_CXX \ +module_expsym_cmds_CXX \ +export_symbols_cmds_CXX \ +prelink_cmds_CXX \ +postlink_cmds_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +ac_aux_dir='$ac_aux_dir' + +# See if we are running on zsh, and set the options that allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='$PACKAGE' + VERSION='$VERSION' + RM='$RM' + ofile='$ofile' + + + + + + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "include/pqxx/config.h") CONFIG_HEADERS="$CONFIG_HEADERS include/pqxx/config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "config/Makefile") CONFIG_FILES="$CONFIG_FILES config/Makefile" ;; + "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; + "doc/Doxyfile") CONFIG_FILES="$CONFIG_FILES doc/Doxyfile" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; + "tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;; + "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; + "include/pqxx/Makefile") CONFIG_FILES="$CONFIG_FILES include/pqxx/Makefile" ;; + "libpqxx.pc") CONFIG_FILES="$CONFIG_FILES libpqxx.pc" ;; + "configitems") CONFIG_COMMANDS="$CONFIG_COMMANDS configitems" ;; + "compile_flags") CONFIG_FILES="$CONFIG_FILES compile_flags" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + case $CONFIG_FILES in #( + *\'*) : + eval set x "$CONFIG_FILES" ;; #( + *) : + set x $CONFIG_FILES ;; #( + *) : + ;; +esac + shift + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf + do + # Strip MF so we end up with the name of the file. + am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`$as_dirname -- "$am_mf" || +$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$am_mf" : 'X\(//\)[^/]' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$am_mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + am_filepart=`$as_basename -- "$am_mf" || +$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$am_mf" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { echo "$as_me:$LINENO: cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles" >&5 + (cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } || am_rc=$? + done + if test $am_rc -ne 0; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. If GNU make was not used, consider + re-running the configure script with MAKE=\"gmake\" (or whatever is + necessary). You can also try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking). +See \`config.log' for more details" "$LINENO" 5; } + fi + { am_dirpart=; unset am_dirpart;} + { am_filepart=; unset am_filepart;} + { am_mf=; unset am_mf;} + { am_rc=; unset am_rc;} + rm -f conftest-deps.mk +} + ;; + "libtool":C) + + # See if we are running on zsh, and set the options that allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST + fi + + cfgfile=${ofile}T + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL +# Generated automatically by $as_me ($PACKAGE) $VERSION +# NOTE: Changes made to this file will be lost: look at ltmain.sh. + +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit, 1996 + +# Copyright (C) 2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program or library that is built +# using GNU Libtool, you may include this file under the same +# distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +# The names of the tagged configurations supported by this script. +available_tags='CXX ' + +# Configured defaults for sys_lib_dlsearch_path munging. +: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} + +# ### BEGIN LIBTOOL CONFIG + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shared archive member basename,for filename based shared library versioning on AIX. +shared_archive_member_spec=$shared_archive_member_spec + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The PATH separator for the build system. +PATH_SEPARATOR=$lt_PATH_SEPARATOR + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# convert \$build file names to \$host format. +to_host_file_cmd=$lt_cv_to_host_file_cmd + +# convert \$build files to toolchain format. +to_tool_file_cmd=$lt_cv_to_tool_file_cmd + +# An object symbol dumper. +OBJDUMP=$lt_OBJDUMP + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method = "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# How to find potential files when deplibs_check_method = "file_magic". +file_magic_glob=$lt_file_magic_glob + +# Find potential files using nocaseglob when deplibs_check_method = "file_magic". +want_nocaseglob=$lt_want_nocaseglob + +# DLL creation program. +DLLTOOL=$lt_DLLTOOL + +# Command to associate shared and link libraries. +sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd + +# The archiver. +AR=$lt_AR + +# Flags to create an archive. +AR_FLAGS=$lt_AR_FLAGS + +# How to feed a file listing to the archiver. +archiver_list_spec=$lt_archiver_list_spec + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm into a list of symbols to manually relocate. +global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# The name lister interface. +nm_interface=$lt_lt_cv_nm_interface + +# Specify filename containing input files for \$NM. +nm_file_list_spec=$lt_nm_file_list_spec + +# The root where to search for dependent libraries,and where our libraries should be installed. +lt_sysroot=$lt_sysroot + +# Command to truncate a binary pipe. +lt_truncate_bin=$lt_lt_cv_truncate_bin + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Manifest tool. +MANIFEST_TOOL=$lt_MANIFEST_TOOL + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Detected run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path + +# Explicit LT_SYS_LIBRARY_PATH set during ./configure time. +configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \$shlibpath_var if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects +postdep_objects=$lt_postdep_objects +predeps=$lt_predeps +postdeps=$lt_postdeps + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path + +# ### END LIBTOOL CONFIG + +_LT_EOF + + cat <<'_LT_EOF' >> "$cfgfile" + +# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE + +# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x$2 in + x) + ;; + *:) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" + ;; + x:*) + eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" + ;; + *) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" + ;; + esac +} + + +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in $*""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} + + +# ### END FUNCTIONS SHARED WITH CONFIGURE + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain=$ac_aux_dir/ltmain.sh + + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" + + + cat <<_LT_EOF >> "$ofile" + +# ### BEGIN LIBTOOL TAG CONFIG: CXX + +# The linker used to build libraries. +LD=$lt_LD_CXX + +# How to create reloadable object files. +reload_flag=$lt_reload_flag_CXX +reload_cmds=$lt_reload_cmds_CXX + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds_CXX + +# A language specific compiler. +CC=$lt_compiler_CXX + +# Is the compiler the GNU compiler? +with_gcc=$GCC_CXX + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object_CXX + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld_CXX + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct_CXX + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \$shlibpath_var if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute_CXX + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic_CXX + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath_CXX + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds_CXX + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds_CXX + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec_CXX + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects_CXX +postdep_objects=$lt_postdep_objects_CXX +predeps=$lt_predeps_CXX +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# ### END LIBTOOL TAG CONFIG: CXX +_LT_EOF + + ;; + "configitems":C) "${srcdir}/tools/splitconfig" "${srcdir}" ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/ext/libpqxx-7.7.3/configure.ac b/ext/libpqxx-7.7.3/configure.ac new file mode 100644 index 000000000..d6351faf2 --- /dev/null +++ b/ext/libpqxx-7.7.3/configure.ac @@ -0,0 +1,738 @@ +# Generate configure script for libpqxx. This needs the autoconf archive +# package installed. (The configure script itself does not require it though.) + +AC_PREREQ(2.69) +AC_INIT( + libpqxx, + [m4_esyscmd_s([./tools/extract_version])], + [Jeroen T. Vermeulen]) +AC_LANG(C++) + +AC_CONFIG_SRCDIR([src/connection.cxx]) +AC_CONFIG_AUX_DIR(config) +AC_CONFIG_MACRO_DIR([config/m4]) +AM_INIT_AUTOMAKE([subdir-objects]) + +PQXX_ABI=m4_esyscmd_s([./tools/extract_version --abi]) +AC_SUBST(PQXXVERSION, $PACKAGE_VERSION) +AC_SUBST(PQXX_ABI) + +AC_CONFIG_HEADER([include/pqxx/config.h]) + +# Default prefix for installs. +AC_PREFIX_DEFAULT(/usr/local) + + +# Read test programme from config-test. +AC_DEFUN([read_test], [AC_LANG_SOURCE( + esyscmd(tools/m4esc.py --input=config-tests/$1))]) + + +# Checks for programs. +AC_PROG_CXX +AC_PROG_INSTALL +AC_DISABLE_SHARED +AC_PROG_LIBTOOL +AC_PROG_MAKE_SET +AC_PATH_PROG([MKDIR], [mkdir]) + +# Documentation. +AC_ARG_ENABLE( + documentation, + [AS_HELP_STRING([--enable-documentation], [Generate documentation])], + [], + [enable_documentation=auto]) +AC_ARG_VAR([DOXYGEN], + [Path to doxygen needed to build reference documentation]) +AC_PATH_TOOL([DOXYGEN], [doxygen], [nodoxygen]) +AC_ARG_VAR([HAVE_DOT], + [Variable used by doxygen to declare availibility of dot]) +AC_CHECK_TOOL([HAVE_DOT], [dot], [YES], [NO]) +AS_IF([test "$enable_documentation" = "yes" && test "$DOXYGEN" = "nodoxygen"], + [AC_MSG_ERROR([could not find tools necessary to build documentation])]) +AM_CONDITIONAL([BUILD_REFERENCE], + [test "$enable_documentation" != "no" -a "$DOXYGEN" != "nodoxygen"]) + +AM_MAINTAINER_MODE + +# See if we want stricter compiler warnings. +AC_MSG_CHECKING([maintainer mode]) +AC_ARG_ENABLE(maintainer-mode) +AC_MSG_RESULT(${enable_maintainer_mode}) + +# See if we want runtime debug checking. +AC_MSG_CHECKING([audit]) +AC_ARG_ENABLE(audit) +AC_MSG_RESULT(${enable_audit}) + +# See if we want "suggestions," such as "this class could be final." +# (The suggestions are often useful, but can also easily be wrong.) +AC_MSG_CHECKING([suggest]) +AC_ARG_ENABLE(suggest) +AC_MSG_RESULT(${enable_suggest}) + + +AC_ARG_ENABLE(shared) +AS_IF( + [test "${shared}" = "yes" ], + [CPPFLAGS="$CPPFLAGS -DPQXX_SHARED"]) + + +# Add options to compiler command line, if compiler accepts them. +add_compiler_opts_if_ok() { + for option in $* + do + ACO_SAVE_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $option" + AC_MSG_CHECKING([whether $CXX accepts $option]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([], [])], + has_option=yes, + has_option=no) + AC_MSG_RESULT($has_option) + AS_IF( + [test "$has_option" = "no" ], + [CXXFLAGS="$ACO_SAVE_CXXFLAGS"]) + done +} + + +# Add options to compiler command line, unconditionally. +add_compiler_opts() { + CXXFLAGS="$CXXFLAGS $*" +} + + +# It's tempting to use Autoconf Archive's AX_CXX_COMPILE_STDCXX_17 for this, +# but it's 2022 and the C++20 equivalent isn't quite ready for use. +# Seems simpler and more reliable for the user to arrange for the desired +# language versions by setting the appropriate option for their compiler. +AC_MSG_CHECKING([for sufficient C++ language/library level]) +sufficient_cxx=yes +AC_COMPILE_IFELSE( + [AC_LANG_SOURCE([ + #if __cplusplus < 201611L + #error "Need C++17 or better." + #endif + ])], + sufficient_cxx=yes, + sufficient_cxx=no) +AC_MSG_RESULT($sufficient_cxx) +if test "$sufficient_cxx" != "yes" +then + AC_MSG_ERROR([This libpqxx version needs at least C++17.]) +fi + + +# Let's try to get the compiler to be helpful. +# +# (Omit options -Weffc++ and -Wabi because they currently yield too many +# warnings in gcc's standard headers; omit -Wunreachable-code because it isn't +# always right) +if test "$GCC" = "yes" +then + # In maintainer mode, enable all the warning options we can. + if test "$enable_maintainer_mode" = "yes" + then + # "Eternal" (FLW) g++ options. These have been around for + # ages, and both g++ and clang++ support them. Don't bother + # checking for support; just add them to the compiler options. + add_compiler_opts \ + -fstrict-enums \ + -Werror \ + -Wall \ + -pedantic \ + -Wcast-align \ + -Wcast-qual \ + -Wconversion \ + -Wctor-dtor-privacy \ + -Wendif-labels \ + -Wextra \ + -Wfloat-equal \ + -Wformat=2 \ + -Wformat-security \ + -Wmissing-include-dirs \ + -Wno-div-by-zero \ + -Wnon-virtual-dtor \ + -Wold-style-cast \ + -Woverlength-strings \ + -Woverloaded-virtual \ + -Wpointer-arith \ + -Wredundant-decls \ + -Wshadow \ + -Wsign-promo \ + -Wundef \ + -Wunused \ + -Wwrite-strings + + # "Iffy" g++ options. Some reasonably current g++-like + # compilers may not support these. + add_compiler_opts_if_ok \ + -fnothrow-opt \ + -Wattribute-alias=2 \ + -Wextra-semi \ + -Wlogical-op \ + -Wmismatched-tags \ + -Wnoexcept \ + -Wredundant-tags \ + -Wrestrict \ + -Wstringop-overflow \ + -Wzero-as-null-pointer-constant \ + -Warray-bounds=2 \ + -Wduplicated-branches \ + -Wduplicated-cond \ + -Wsuggest-attribute=noreturn \ + -Wsuggest-override \ + -Wtrampolines + fi + + # In "audit," enable all runtime checks we can. + if test "$enable_audit" = "yes" + then + add_compiler_opts_if_ok \ + -D_FORTIFY_SOURCE=2 \ + -fsanitize=address \ + -fsanitize-address-use-after-scope \ + -fsanitize=alignment \ + -fsanitize=bool \ + -fsanitize=bounds \ + -fsanitize=bounds-strict \ + -fsanitize=builtin \ + -fsanitize=enum \ + -fsanitize=float-cast-overflow \ + -fsanitize=float-divide-by-zero \ + -fsanitize=integer-divide-by-zero \ + -fsanitize=leak \ + -fsanitize=nonnull-attribute \ + -fsanitize=null \ + -fsanitize=object-size \ + -fsanitize=pointer-compare \ + -fsanitize=pointer-overflow \ + -fsanitize=pointer-subtract \ + -fsanitize=return \ + -fsanitize=returns-nonnull-attribute \ + -fsanitize=shift \ + -fsanitize=shift-base \ + -fsanitize=shift-exponent \ + -fsanitize=signed-integer-overflow \ + -fsanitize=undefined \ + -fsanitize=unreachable \ + -fsanitize=vla-bound \ + -fsanitize=vptr \ + -fstack-protector-all + fi + + # In "suggest" mode, enable a bunch of code suggestions. + if test "$enable_suggest" = "yes" + then + add_compiler_opts_if_ok \ + -Wsuggest-attribute=cold \ + -Wsuggest-attribute=const \ + -Wsuggest-attribute=malloc \ + -Wsuggest-attribute=pure \ + -Wsuggest-final-types \ + -Wsuggest-final-methods + fi + +AC_MSG_CHECKING([g++ visibility attribute]) +gcc_visibility=yes +SAVE_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$CXXFLAGS -Werror" +AC_COMPILE_IFELSE( + [read_test(gcc_visibility.cxx)], + AC_DEFINE( + [PQXX_HAVE_GCC_VISIBILITY], + 1, + [Define if g++ supports visibility attribute.]), + gcc_visibility=no) +AC_MSG_RESULT($gcc_visibility) +CXXFLAGS="$SAVE_CXXFLAGS" +if test "$gcc_visibility" = "yes" +then + # Make internal definitions accessible only to the library itself. + # Only definitions marked PQXX_LIBEXPORT will be accessible. + add_compiler_opts -fvisibility=hidden + add_compiler_opts -fvisibility-inlines-hidden +fi + +AC_MSG_CHECKING([g++ pure attribute]) +gcc_pure=yes +AC_COMPILE_IFELSE( + [read_test(gcc_pure.cxx)], + AC_DEFINE( + [PQXX_HAVE_GCC_PURE], + 1, + [Define if g++ supports pure attribute]), + gcc_pure=no) +AC_MSG_RESULT($gcc_pure) + +fi # End of gcc-specific part. + + +# Check for __cxa_demangle. +AC_MSG_CHECKING([__cxa_demangle]) +cxa_demangle=yes +AC_COMPILE_IFELSE( + [read_test(cxa_demangle.cxx)], + AC_DEFINE( + [PQXX_HAVE_CXA_DEMANGLE], + 1, + [Define if compiler supports __cxa_demangle]), + cxa_demangle=no) +AC_MSG_RESULT($cxa_demangle) + + +# Check for sufficient Concepts support, introduced with C++20. +AC_MSG_CHECKING([concepts]) +concepts=yes +AC_COMPILE_IFELSE( + [read_test(concepts.cxx)], + AC_DEFINE( + [PQXX_HAVE_CONCEPTS], + 1, + [Define if compiler supports Concepts and header.]), + concepts=no) +AC_MSG_RESULT($concepts) + + +# Check for C++20 std::span. +AC_MSG_CHECKING([std::span]) +span=yes +AC_COMPILE_IFELSE( + [read_test(span.cxx)], + AC_DEFINE([PQXX_HAVE_SPAN], 1, [Define if compiler has std::span.]), + span=no) +AC_MSG_RESULT($span) + + +# Check for multidimensional subscript operator support. Proposed for C++23. +AC_MSG_CHECKING([for multidimensional subscript operator support]) +multidim_subscript=yes +AC_COMPILE_IFELSE( + [read_test(multidim-subscript.cxx)], + AC_DEFINE( + [PQXX_HAVE_MULTIDIMENSIONAL_SUBSCRIPT], 1, + [Define if operator[] can take multiple arguments.]), + multidim_subscript=no) +AC_MSG_RESULT($multidim_subscript) + + +AC_MSG_CHECKING([for strerror_r()]) +strerror_r=yes +AC_LINK_IFELSE( + [read_test(strerror_r.cxx)], + AC_DEFINE( + [PQXX_HAVE_STRERROR_R], + 1, + [Define if strerror_r() is available.]), + strerror_r=no) +AC_MSG_RESULT($strerror_r) + + + +AC_MSG_CHECKING([for strerror_s()]) +strerror_s=yes +AC_LINK_IFELSE( + [read_test(strerror_s.cxx)], + AC_DEFINE( + [PQXX_HAVE_STRERROR_S], + 1, + [Define if strerror_s() is available.]), + strerror_s=no) +AC_MSG_RESULT($strerror_s) + + +AC_MSG_CHECKING([for std::chrono::year_month_day etc]) +year_month_day=yes +AC_LINK_IFELSE( + [read_test(year_month_day.cxx)], + AC_DEFINE( + [PQXX_HAVE_YEAR_MONTH_DAY], + 1, + [Define if std::chrono has year_month_day etc.]), + year_month_day=no) +AC_MSG_RESULT($year_month_day) + + +# Check for [[likely]] and [[unlikely]] attributes. +AC_MSG_CHECKING([attributes "likely" and "unlikely".]) +likely=yes +AC_COMPILE_IFELSE( + [read_test(likely.cxx)], + AC_DEFINE([PQXX_HAVE_LIKELY], 1, [Define if likely & unlikely work.]), + likely=no) +AC_MSG_RESULT($likely) + + +# It's mid-2019, and gcc's charconv supports integers but not yet floats. +# So for now, we test for int and float conversion... separately. +# +# It's worse for older clang versions, which compile the integer conversions +# but then fail at link time because of a missing symbol "__muloti4" with the +# "long long" version. I couldn't resolve that symbol by adding -lm either. +# So don't just compile these tests; link them as well. +AC_MSG_CHECKING([for C++17 charconv integer conversion]) +have_charconv_int=yes +AC_LINK_IFELSE( + [read_test(charconv_int.cxx)], + AC_DEFINE( + [PQXX_HAVE_CHARCONV_INT], + 1, + [Define if supports integer conversion.]), + have_charconv_int=no) +AC_MSG_RESULT($have_charconv_int) + +AC_MSG_CHECKING([for C++17 charconv floating-point conversion]) +have_charconv_float=yes +AC_LINK_IFELSE( + [read_test(charconv_float.cxx)], + AC_DEFINE( + [PQXX_HAVE_CHARCONV_FLOAT], + 1, + [Define if supports floating-point conversion.]), + have_charconv_float=no) +AC_MSG_RESULT($have_charconv_float) + +# As per #262, clang with libcxxrt does not support thread_local on non-POD +# objects. Luckily we can live without those, it's just less efficient. +AC_MSG_CHECKING([for full thread_local support]) +have_thread_local=yes +AC_LINK_IFELSE( + [read_test(thread_local.cxx)], + AC_DEFINE( + [PQXX_HAVE_THREAD_LOCAL], + 1, + [Define if thread_local is fully supported.]), + have_thread_local=no) +AC_MSG_RESULT($have_thread_local) + +AC_MSG_CHECKING([for std::this_thread::sleep_for]) +have_sleep_for=yes +AC_LINK_IFELSE( + [read_test(sleep_for.cxx)], + AC_DEFINE( + [PQXX_HAVE_SLEEP_FOR], + 1, + [Define if std::this_thread::sleep_for works.]), + have_sleep_for=no) +AC_MSG_RESULT($have_sleep_for) + + +AC_MSG_CHECKING([for std::cmp_greater, std::cmp_less_equal, etc]) +have_cmp=yes +AC_COMPILE_IFELSE( + [read_test(cmp.cxx)], + AC_DEFINE( + [PQXX_HAVE_CMP], + 1, + [Define if compiler has C++20 std::cmp_greater etc.]), + have_cmp=no) +AC_MSG_RESULT($have_cmp) + + +# Doing my own check for poll(). There's one built into autoconf-archive, but +# it produces warnings in C++ (about unnecessarily using "struct", and using 0 +# as a null pointer constant). In maintainer mode, those warnings turn into +# errors. +AC_MSG_CHECKING([for poll()]) +ax_cv_have_poll=yes +AC_LINK_IFELSE( + [read_test(poll.cxx)], + AC_DEFINE( + [PQXX_HAVE_POLL], + 1, + [Define if poll() is available.]), + ax_cv_have_poll=no) +AC_MSG_RESULT($ax_cv_have_poll) + +if test "$ax_cv_have_poll" != "yes" +then +# No poll(); we'll fall back to select(). + +# Some systems keep select() in a separate library which is not linked by +# default. See if we need one of those. +socklibok=no +AC_SEARCH_LIBS(select, socket nsl ws2_32 wsock32 winsock, [socklibok=yes]) + +# Microsoft proprietary libraries do not work with code that is generated with +# autoconf's SEARCH_LIBS macro, so we need to check manually and just use the +# first socket library available. +# We only do this if select() is not available by other means, to avoid picking +# up an unnecessary Windows compatibility library on a non-Windows system. +for l in ws2_32 wsock32 winsock +do + if test "${socklibok}" != "yes" + then + AC_CHECK_LIB($l,main,LIBS="$LIBS -l$l";[socklibok=yes]) + fi +done + +if test "${socklibok}" != "yes" +then + AC_MSG_ERROR([ +Could not figure out how to link a simple sockets-based program. Please read +the config.log file for more clues as to why this failed. +]) +fi + +fi # No poll() + + +# Find PostgreSQL includes and libraries +AC_PATH_PROG([PKG_CONFIG], [pkg-config]) +AC_PATH_PROGS(PG_CONFIG, pg_config) + +AC_ARG_WITH( + [postgres-include], + [AS_HELP_STRING( + [--with-postgres-include=DIR], + [Use PostgreSQL includes from DIR. Defaults to querying pg_config or pkg-config, whichever is available.])], + AS_IF( + [test "x$with_postgres_include" = "xyes"], + [with_postgres_include=""])) + +if test -n "$with_postgres_include" +then + POSTGRES_INCLUDE="-I$with_postgres_include" +else + if test -x "$PKG_CONFIG" || test -x "$PG_CONFIG" + then + # We should prefer pkg-config over pg_config, but there seems to be a + # problem in pkg-config 1.6.3. Until that's been resolved (#291), go + # with pg_config if we can. + if test -x "$PG_CONFIG" + then + # From pg_config we can either get the C compiler options used to + # compile postgres, which isn't quite what we want; or we can get + # the headers directory, without the full option. That's something + # we can work with. The compiler must support the "-I" option for + # that, but both scripts assume that anyway. + POSTGRES_INCLUDE="-I$($PG_CONFIG --includedir)" + else + # From pkg-config we can get the compiler options to extend the + # include path. We use that. + POSTGRES_INCLUDE=$($PKG_CONFIG libpq --cflags-only-I) + fi + AC_MSG_NOTICE([finding PostgreSQL headers using $POSTGRES_INCLUDE]) + else + POSTGRES_INCLUDE="" + + # We have nothing to tell us where the libpq headers are. That's fine + # if the compiler can find it, but if not, fail here. + AC_CHECK_HEADER( + [libpq-fe.h], + [], + [AC_MSG_ERROR([ +Can't find the main PostgreSQL client header, libpq-fe.h. Make sure that it +is installed, and either use the --with-postgres-include option or install +pkg-config. +])]) + fi +fi +AC_SUBST(POSTGRES_INCLUDE) + +# Add the compiler option so we can compile configure tests which rely on the +# libpq headers. +CPPFLAGS="$CPPFLAGS $POSTGRES_INCLUDE" + + +AC_ARG_WITH( + [postgres-lib], + [AS_HELP_STRING( + [--with-postgres-lib=DIR], + [Use PostgreSQL libraries from DIR. Defaults to querying pg_config.])], + AS_IF( + [test "x$with_postgres_lib" = "xyes"], + [with_postgres_lib=""])) + +# If no --with-postgres-lib was given, and we have pkg-config, use that. +AS_IF( + [test -z "$with_postgres_lib" -a -x "$PKG_CONFIG"], + [with_postgres_lib=$($PKG_CONFIG libpq --libs-only-L | sed 's/^-L//')]) + +# pg_config is deprecated, but for some users it may still provide the only +# right answer. For instance, `pkg-config` may not know where `libpq` is +# installed. +AS_IF( + [test -z "$with_postgres_lib" -a -x "$PG_CONFIG"], + [with_postgres_lib=$($PG_CONFIG --libdir)]) + +AS_IF( + [test -n "$with_postgres_lib"], + [AC_MSG_NOTICE([using PostgreSQL libraries at $with_postgres_lib])], + [AC_MSG_NOTICE([using PostgreSQL libraries in default location])]) + +AC_SUBST(with_postgres_lib) + + +AC_CHECK_HEADER( + [libpq-fe.h], + [], + [AC_MSG_ERROR([ +Can't find the main PostgreSQL client header, libpq-fe.h. Are you sure the +libpq headers are installed correctly, and that we've got the right path? +])]) + + +AC_MSG_CHECKING([for ability to compile source files using libpq]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include]], + [[PQexec(nullptr,"")]] + )], + [], + [AC_MSG_ERROR([ +Could not compile a call to a basic libpq function. There must be something +seriously wrong with the headers that "pg_config --includedir" or "pkg-config +libpq --cflags" pointed to; the contents of config.log may give you a clue +about the nature of the failure. +Source including the libpq header libpq-fe.h can be compiled, but a call to the +most basic libpq function PQexec() failed to compile successfully. This is the +litmus test for a working libpq. +])]) +AC_MSG_RESULT(yes) + + +if test "x${with_postgres_lib}" = "x"; then + with_postgres_libpath="" +else + with_postgres_libpath="-L${with_postgres_lib}" +fi +LDFLAGS="$LDFLAGS ${with_postgres_libpath}" + +AC_CHECK_LIB( + [pq], + [PQexec], + [], + [AC_MSG_ERROR([ +Did not find the PQexec() function in libpq. This is the litmus test for a +working libpq installation. + +A source file using the PQexec() function did compile without problems, and the +libpq library is available for linking, but for some reason a call to PQexec() +failed to link properly to the libpq library. This may be because the libpq +library file is damaged, or in some incorrect format, or if your libpq is much +more recent than libpqxx version $PQXX_ABI, perhaps libpq has undergone a +radical ABI change. + +The last parts of config.log may give you a clue as to what really went wrong, +but be warned that this is no easy reading. Look for the last error message +occurring in the file. +])], + ${with_postgres_libpath}) + + +# PQencryptPasswordConn was added in postgres 10. +AC_MSG_CHECKING([for PQencryptPasswordConn]) +have_pqencryptpasswordconn=yes +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include], + [ + extern PGconn *conn; + PQencryptPasswordConn( + conn, "pwd", "user", "scram-sha-256") + ] + )], + AC_DEFINE( + [PQXX_HAVE_PQENCRYPTPASSWORDCONN], + 1, + [Define if libpq has PQencryptPasswordConn (since pg 10).]), + [have_pqencryptpasswordconn=no]) +AC_MSG_RESULT($have_pqencryptpasswordconn) + + +# "Pipeline mode" was introduced in libpq 14. +AC_MSG_CHECKING([for libpq pipeline mode]) +have_pq_pipeline=yes +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include], + [ + extern PGconn *conn; + PQenterPipelineMode(conn) + ] + )], + AC_DEFINE( + [PQXX_HAVE_PQ_PIPELINE], + 1, + [Define if libpq has pipeline mode (since pg 14).]), + [have_pq_pipeline=no]) +AC_MSG_RESULT($have_pq_pipeline) + + +AC_MSG_CHECKING([for usable std::filesystem::path]) +have_path=yes +AC_COMPILE_IFELSE( + [read_test(fs.cxx)], + AC_DEFINE( + [PQXX_HAVE_PATH], + 1, + [Define if compiler has usable std::filesystem::path.]), + have_path=no) +AC_MSG_RESULT($have_path) + + +AC_MSG_CHECKING([whether we need a link option for support]) +LIBS_SAVE="$LIBS" +found_fslib=no +for l in '' '-lstdc++fs' '-lc++fs' +do + if test "$found_fslib" != "yes" + then + LIBS="$LIBS $l" + AC_LINK_IFELSE( + [read_test(need_fslib.cxx)], + [found_fslib=yes], + [LIBS="$LIBS_SAVE"]) + if test "$found_fslib" = "yes" + then + result_msg="$l" + # (And keep our current value of $LIBS.) + fi + fi +done + +if test "$found_fslib" != "yes" +then + AC_MSG_ERROR([ +There seems to be support, but I could not figure out now to make +it work. You'll have to add set your own build options for this. + ]) +fi +AC_MSG_RESULT($result_msg) + + +# Remove redundant occurrances of -lpq +LIBS=[$(echo "$LIBS" | sed -e 's/-lpq * -lpq\>/-lpq/g')] + + +AC_MSG_CHECKING([that type of libpq's Oid is as expected]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [ + #include + #include"${srcdir}/include/pqxx/internal/libpq-forward.hxx" + extern void f(pqxx::oid&); + ], + [Oid o;f(o)], + )], + [], + [AC_MSG_ERROR([ +The Oid typedef in libpq has changed. Please notify the libpqxx authors of the +change! +])]) +AC_MSG_RESULT(yes) + + +AC_PROG_MAKE_SET + +AC_CONFIG_FILES([ + Makefile config/Makefile doc/Makefile doc/Doxyfile src/Makefile + test/Makefile tools/Makefile include/Makefile include/pqxx/Makefile + libpqxx.pc]) + + +AC_CONFIG_COMMANDS([configitems], ["${srcdir}/tools/splitconfig" "${srcdir}"]) + +AC_OUTPUT(compile_flags) diff --git a/ext/libpqxx-7.7.3/doc/CMakeLists.txt b/ext/libpqxx-7.7.3/doc/CMakeLists.txt new file mode 100644 index 000000000..d48d2a6f0 --- /dev/null +++ b/ext/libpqxx-7.7.3/doc/CMakeLists.txt @@ -0,0 +1,50 @@ +find_program(HAVE_DOXYGEN doxygen) + +if(NOT HAVE_DOXYGEN) + message(FATAL_ERROR "***************************************************** +Doxygen not found. +Install it, or configure with -DBUILD_DOC=OFF +*****************************************************" + ) +endif() + +set(PQXXVERSION "${CMAKE_PROJECT_VERSION}") +set(top_srcdir "${PROJECT_SOURCE_DIR}") +set(PQXX_ABI "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") +set(PQXX_MAJOR "${PROJECT_VERSION_MAJOR}") +set(PQXX_MINOR "${PROJECT_VERSION_MINOR}") + +find_program(HAVE_DOT dot) +if(HAVE_DOT) + set(HAVE_DOT YES) +else() + set(HAVE_DOT NO) +endif() + +configure_file(Doxyfile.in Doxyfile) + +if(HAVE_DOXYGEN) + file( + GLOB DOXYGEN_SOURCES + "${PROJECT_SOURCE_DIR}/include/pqxx/*.hxx" + "${PROJECT_SOURCE_DIR}/include/pqxx/doc/*.md" + "${PROJECT_SOURCE_DIR}/*.cxx" + ) + set(DOXYGEN_STAMP_FILE "${CMAKE_CURRENT_BINARY_DIR}/doxygen.stamp") + add_custom_command(OUTPUT ${DOXYGEN_STAMP_FILE} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/html + COMMAND doxygen Doxyfile + COMMAND ${CMAKE_COMMAND} -E touch ${DOXYGEN_STAMP_FILE} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile ${DOXYGEN_SOURCES} + COMMENT "Generate API documentation" + VERBATIM + ) + add_custom_target(doxygen ALL + DEPENDS ${DOXYGEN_STAMP_FILE} + SOURCES ${DOXYGEN_SOURCES} + ) + install( + DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html + DESTINATION ${CMAKE_INSTALL_DOCDIR}/html + ) +endif() diff --git a/ext/libpqxx-7.7.3/doc/Doxyfile.in b/ext/libpqxx-7.7.3/doc/Doxyfile.in new file mode 100644 index 000000000..f349fe80c --- /dev/null +++ b/ext/libpqxx-7.7.3/doc/Doxyfile.in @@ -0,0 +1,1280 @@ +# Doxyfile 1.5.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = libpqxx + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @PQXXVERSION@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = html + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, +# and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = ../ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = NO + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = NO + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = NO + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @top_srcdir@/include/pqxx \ + @top_srcdir@/include/pqxx/doc \ + @top_srcdir@/src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.cxx \ + *.hxx \ + *.h \ + *.md + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = *.o \ + *.lo \ + *.a \ + .cvsignore + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = . + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = YES + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = "../include" "@top_srcdir@/include" + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = *.h *.hxx + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = PQXX_ABI="@PQXX_ABI@" \ + PQXX_LIBEXPORT= \ + PQXX_NOVTABLE= \ + PQXX_PRIVATE= \ + PQXX_TYPENAME=typename \ + PQXX_VERSION="@PQXXVERSION@" \ + PQXX_MAJOR=@PQXX_MAJOR@ \ + PQXX_MINOR=@PQXX_MINOR@ + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = @HAVE_DOT@ + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is enabled by default, which results in a transparent +# background. Warning: Depending on the platform used, enabling this option +# may lead to badly anti-aliased labels on the edges of a graph (i.e. they +# become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/ext/libpqxx-7.7.3/doc/Makefile.am b/ext/libpqxx-7.7.3/doc/Makefile.am new file mode 100644 index 000000000..38caa9181 --- /dev/null +++ b/ext/libpqxx-7.7.3/doc/Makefile.am @@ -0,0 +1,51 @@ +MAINTAINERCLEANFILES = Makefile.in + +maintainer-clean-local: + $(RM) -rf html + $(RM) reference-stamp + $(MKDIR) html + +EXTRA_DIST= Doxyfile.in libpqxx.xml reference-stamp + +all-local: docs + +if BUILD_REFERENCE +DOCS = reference +else +DOCS = +endif + +if MAINTAINER_MODE +REFERENCE_STAMP_DEP = ../src/libpqxx.la +else +REFERENCE_STAMP_DEP = +endif + +docs: $(DOCS) + +reference: reference-stamp +reference-stamp: Doxyfile.in $(REFERENCE_STAMP_DEP) + if [ -x "$(DOXYGEN)" ]; then \ + $(MKDIR_P) html; \ + $(DOXYGEN) Doxyfile; \ + touch $@; \ + else \ + echo >&2; \ + echo >&2 "*****************************************************"; \ + echo >&2; \ + echo >&2 "Doxygen not found."; \ + echo >&2 "Install it, or configure with --disable-documentation"; \ + echo >&2; \ + echo >&2 "*****************************************************"; \ + exit 1; \ + fi + +../src/libpqxx.la: + cd ../src; \ + $(MAKE) libpqxx.la + + +dist-hook: reference + if [ -d $(srcdir)/html ]; then \ + cp -pR html $(distdir)/ ; \ + fi diff --git a/ext/libpqxx-7.7.3/doc/Makefile.in b/ext/libpqxx-7.7.3/doc/Makefile.in new file mode 100644 index 000000000..835845bf7 --- /dev/null +++ b/ext/libpqxx-7.7.3/doc/Makefile.in @@ -0,0 +1,507 @@ +# Makefile.in generated by automake 1.16.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = doc +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/config/m4/libtool.m4 \ + $(top_srcdir)/config/m4/ltoptions.m4 \ + $(top_srcdir)/config/m4/ltsugar.m4 \ + $(top_srcdir)/config/m4/ltversion.m4 \ + $(top_srcdir)/config/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/include/pqxx/config.h +CONFIG_CLEAN_FILES = Doxyfile +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Doxyfile.in $(srcdir)/Makefile.in \ + $(top_srcdir)/config/mkinstalldirs +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PG_CONFIG = @PG_CONFIG@ +PKG_CONFIG = @PKG_CONFIG@ +POSTGRES_INCLUDE = @POSTGRES_INCLUDE@ +PQXXVERSION = @PQXXVERSION@ +PQXX_ABI = @PQXX_ABI@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_postgres_lib = @with_postgres_lib@ +MAINTAINERCLEANFILES = Makefile.in +EXTRA_DIST = Doxyfile.in libpqxx.xml reference-stamp +@BUILD_REFERENCE_FALSE@DOCS = +@BUILD_REFERENCE_TRUE@DOCS = reference +@MAINTAINER_MODE_FALSE@REFERENCE_STAMP_DEP = +@MAINTAINER_MODE_TRUE@REFERENCE_STAMP_DEP = ../src/libpqxx.la +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +Doxyfile: $(top_builddir)/config.status $(srcdir)/Doxyfile.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: check-am +all-am: Makefile all-local +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic \ + maintainer-clean-local + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am all-local check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags-am dist-hook distclean \ + distclean-generic distclean-libtool distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic maintainer-clean-local mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +maintainer-clean-local: + $(RM) -rf html + $(RM) reference-stamp + $(MKDIR) html + +all-local: docs + +docs: $(DOCS) + +reference: reference-stamp +reference-stamp: Doxyfile.in $(REFERENCE_STAMP_DEP) + if [ -x "$(DOXYGEN)" ]; then \ + $(MKDIR_P) html; \ + $(DOXYGEN) Doxyfile; \ + touch $@; \ + else \ + echo >&2; \ + echo >&2 "*****************************************************"; \ + echo >&2; \ + echo >&2 "Doxygen not found."; \ + echo >&2 "Install it, or configure with --disable-documentation"; \ + echo >&2; \ + echo >&2 "*****************************************************"; \ + exit 1; \ + fi + +../src/libpqxx.la: + cd ../src; \ + $(MAKE) libpqxx.la + +dist-hook: reference + if [ -d $(srcdir)/html ]; then \ + cp -pR html $(distdir)/ ; \ + fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ext/libpqxx-7.7.3/doc/conf.py b/ext/libpqxx-7.7.3/doc/conf.py new file mode 100644 index 000000000..1cac1c2ca --- /dev/null +++ b/ext/libpqxx-7.7.3/doc/conf.py @@ -0,0 +1,199 @@ +# -*- coding: utf-8 -*- +""" +libpqxx documentation build configuration file, created by +sphinx-quickstart on Sun Dec 3 00:43:33 2017. + +This file is execfile()d with the current directory set to its containing dir. + +All configuration values have a default; values that are commented out serve +to show the default. +""" + +import codecs +import os +from subprocess import check_call + + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import sys +# sys.path.insert(0, os.path.abspath(os.path.curdir)) + + +read_the_docs_build = os.environ.get('READTHEDOCS') == 'True' + +if read_the_docs_build: + check_call( + [os.path.join(os.path.curdir, 'configure'), 'CXXFLAGS=-std=c++17 -O0'], + cwd=os.path.pardir) + check_call('doxygen', cwd=os.path.join(os.path.pardir, 'doc')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + ] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'libpqxx' +copyright = u'2000-2022, Jeroen T. Vermeulen' +author = u'Jeroen T. Vermeulen' + + +def read_version(): + """Return version number as specified in the VERSION file.""" + version_file = os.path.join( + os.path.dirname(__file__), os.path.pardir, 'VERSION') + with codecs.open(version_file, encoding='ascii') as stream: + return stream.read().strip() + + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The full version, including alpha/beta/rc tags. +release = read_version() + +# The short X.Y version. +version = '.'.join(release.split('.')[:2]) + +html_title = "libpqxx %s" % release +html_short_title = "libpqxx" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# This is required for the alabaster theme +# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars +html_sidebars = { + '**': [ + 'relations.html', # needs 'show_related': True theme option to display + 'searchbox.html', + ] +} + +# Looks like the setup is that our build generates the HTML itself, and then +# has readthedocs copy the full generated HTML tree to the output directory. +# +# Problem is, that doesn't seem to be working now. This needs debugging. +html_extra_path = ["html"] + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'libpqxxdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ( + master_doc, + 'libpqxx.tex', + u'libpqxx Documentation', + u'Jeroen T. Vermeulen', + 'manual', + ), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'libpqxx', u'libpqxx Documentation', [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'libpqxx', u'libpqxx Documentation', + author, 'libpqxx', "C++ client API for PostgreSQL.", + 'Miscellaneous'), +] diff --git a/ext/libpqxx-7.7.3/doc/index.rst b/ext/libpqxx-7.7.3/doc/index.rst new file mode 100644 index 000000000..30f2cfdc1 --- /dev/null +++ b/ext/libpqxx-7.7.3/doc/index.rst @@ -0,0 +1,20 @@ +.. x documentation master file, created by + sphinx-quickstart on Sun Dec 3 01:30:12 2017. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +libpqxx +======= + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/ext/libpqxx-7.7.3/include/CMakeLists.txt b/ext/libpqxx-7.7.3/include/CMakeLists.txt new file mode 100644 index 000000000..263dd2e9e --- /dev/null +++ b/ext/libpqxx-7.7.3/include/CMakeLists.txt @@ -0,0 +1,68 @@ +# ############################################################################## +# AUTOMATICALLY GENERATED FILE -- DO NOT EDIT. +# +# This file is generated automatically by libpqxx's template2mak.py script, and +# will be rewritten from time to time. +# +# If you modify this file, chances are your modifications will be lost. +# +# The template2mak.py script should be available in the tools directory of the +# libpqxx source archive. +# +# Generated from template './include/CMakeLists.txt.template'. +# ############################################################################## +install( + DIRECTORY pqxx "${PROJECT_BINARY_DIR}/include/pqxx" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + # For each X.hxx, install both X.hxx itself and plain X. + PATTERN *.hxx + # TODO: Is there any way to do this with CMake's globbing? + PATTERN array + PATTERN binarystring + PATTERN blob + PATTERN composite + PATTERN connection + PATTERN cursor + PATTERN dbtransaction + PATTERN errorhandler + PATTERN except + PATTERN field + PATTERN isolation + PATTERN largeobject + PATTERN nontransaction + PATTERN notification + PATTERN params + PATTERN pipeline + PATTERN prepared_statement + PATTERN range + PATTERN result + PATTERN robusttransaction + PATTERN row + PATTERN separated_list + PATTERN strconv + PATTERN stream_from + PATTERN stream_to + PATTERN subtransaction + PATTERN time + PATTERN transaction + PATTERN transaction_base + PATTERN transaction_focus + PATTERN transactor + PATTERN types + PATTERN util + PATTERN version + PATTERN zview + PATTERN internal/*.hxx + PATTERN internal/gates/*.hxx + PATTERN config-public-compiler.h + PATTERN pqxx + PATTERN doc EXCLUDE +) + +install( + DIRECTORY pqxx/doc/ + DESTINATION ${CMAKE_INSTALL_DOCDIR} + FILES_MATCHING + PATTERN *.md +) diff --git a/ext/libpqxx-7.7.3/include/CMakeLists.txt.template b/ext/libpqxx-7.7.3/include/CMakeLists.txt.template new file mode 100644 index 000000000..5ebc6664e --- /dev/null +++ b/ext/libpqxx-7.7.3/include/CMakeLists.txt.template @@ -0,0 +1,23 @@ +install( + DIRECTORY pqxx "${PROJECT_BINARY_DIR}/include/pqxx" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + # For each X.hxx, install both X.hxx itself and plain X. + PATTERN *.hxx + # TODO: Is there any way to do this with CMake's globbing? +###MAKTEMPLATE:FOREACH include/pqxx/*.hxx + PATTERN ###BASENAME### +###MAKTEMPLATE:ENDFOREACH + PATTERN internal/*.hxx + PATTERN internal/gates/*.hxx + PATTERN config-public-compiler.h + PATTERN pqxx + PATTERN doc EXCLUDE +) + +install( + DIRECTORY pqxx/doc/ + DESTINATION ${CMAKE_INSTALL_DOCDIR} + FILES_MATCHING + PATTERN *.md +) diff --git a/ext/libpqxx-7.7.3/include/Makefile.am b/ext/libpqxx-7.7.3/include/Makefile.am new file mode 100644 index 000000000..83d2b1970 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/Makefile.am @@ -0,0 +1,79 @@ +SUBDIRS = pqxx + +nobase_include_HEADERS= pqxx/pqxx \ + pqxx/array pqxx/array.hxx \ + pqxx/binarystring pqxx/binarystring.hxx \ + pqxx/blob pqxx/blob.hxx \ + pqxx/composite pqxx/composite.hxx \ + pqxx/connection pqxx/connection.hxx \ + pqxx/cursor pqxx/cursor.hxx \ + pqxx/dbtransaction pqxx/dbtransaction.hxx \ + pqxx/errorhandler pqxx/errorhandler.hxx \ + pqxx/except pqxx/except.hxx \ + pqxx/field pqxx/field.hxx \ + pqxx/isolation pqxx/isolation.hxx \ + pqxx/largeobject pqxx/largeobject.hxx \ + pqxx/nontransaction pqxx/nontransaction.hxx \ + pqxx/notification pqxx/notification.hxx \ + pqxx/params pqxx/params.hxx \ + pqxx/pipeline pqxx/pipeline.hxx \ + pqxx/prepared_statement pqxx/prepared_statement.hxx \ + pqxx/range pqxx/range.hxx \ + pqxx/result pqxx/result.hxx \ + pqxx/robusttransaction pqxx/robusttransaction.hxx \ + pqxx/separated_list pqxx/separated_list.hxx \ + pqxx/strconv pqxx/strconv.hxx \ + pqxx/stream_from pqxx/stream_from.hxx \ + pqxx/stream_to pqxx/stream_to.hxx \ + pqxx/subtransaction pqxx/subtransaction.hxx \ + pqxx/time pqxx/time.hxx \ + pqxx/transaction pqxx/transaction.hxx \ + pqxx/transaction_base pqxx/transaction_base.hxx \ + pqxx/transaction_focus pqxx/transaction_focus.hxx \ + pqxx/transactor pqxx/transactor.hxx \ + pqxx/row pqxx/row.hxx \ + pqxx/util pqxx/util.hxx \ + pqxx/types pqxx/types.hxx \ + pqxx/zview pqxx/zview.hxx \ + pqxx/version pqxx/version.hxx \ + pqxx/internal/array-composite.hxx \ + pqxx/internal/callgate.hxx \ + pqxx/internal/concat.hxx \ + pqxx/internal/conversions.hxx \ + pqxx/internal/encoding_group.hxx \ + pqxx/internal/encodings.hxx \ + pqxx/internal/header-pre.hxx \ + pqxx/internal/header-post.hxx \ + pqxx/internal/ignore-deprecated-post.hxx \ + pqxx/internal/ignore-deprecated-pre.hxx \ + pqxx/internal/libpq-forward.hxx \ + pqxx/internal/result_iter.hxx \ + pqxx/internal/result_iterator.hxx \ + pqxx/internal/sql_cursor.hxx \ + pqxx/internal/statement_parameters.hxx \ + pqxx/internal/stream_iterator.hxx \ + pqxx/internal/wait.hxx \ + pqxx/internal/gates/connection-errorhandler.hxx \ + pqxx/internal/gates/connection-largeobject.hxx \ + pqxx/internal/gates/connection-notification_receiver.hxx \ + pqxx/internal/gates/connection-pipeline.hxx \ + pqxx/internal/gates/connection-sql_cursor.hxx \ + pqxx/internal/gates/connection-transaction.hxx \ + pqxx/internal/gates/errorhandler-connection.hxx \ + pqxx/internal/gates/icursorstream-icursor_iterator.hxx \ + pqxx/internal/gates/icursor_iterator-icursorstream.hxx \ + pqxx/internal/gates/result-connection.hxx \ + pqxx/internal/gates/result-creation.hxx \ + pqxx/internal/gates/result-pipeline.hxx \ + pqxx/internal/gates/result-sql_cursor.hxx \ + pqxx/internal/gates/transaction-sql_cursor.hxx \ + pqxx/internal/gates/transaction-transaction_focus.hxx + + +nobase_nodist_include_HEADERS = \ + pqxx/config-public-compiler.h + +EXTRA_DIST = \ + pqxx/doc/*.md \ + pqxx/doc/mainpage.md.template \ + pqxx/version.hxx.template diff --git a/ext/libpqxx-7.7.3/include/Makefile.in b/ext/libpqxx-7.7.3/include/Makefile.in new file mode 100644 index 000000000..6b9eac914 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/Makefile.in @@ -0,0 +1,802 @@ +# Makefile.in generated by automake 1.16.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = include +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/config/m4/libtool.m4 \ + $(top_srcdir)/config/m4/ltoptions.m4 \ + $(top_srcdir)/config/m4/ltsugar.m4 \ + $(top_srcdir)/config/m4/ltversion.m4 \ + $(top_srcdir)/config/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(nobase_include_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/include/pqxx/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(includedir)" "$(DESTDIR)$(includedir)" +HEADERS = $(nobase_include_HEADERS) $(nobase_nodist_include_HEADERS) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/config/mkinstalldirs +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PG_CONFIG = @PG_CONFIG@ +PKG_CONFIG = @PKG_CONFIG@ +POSTGRES_INCLUDE = @POSTGRES_INCLUDE@ +PQXXVERSION = @PQXXVERSION@ +PQXX_ABI = @PQXX_ABI@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_postgres_lib = @with_postgres_lib@ +SUBDIRS = pqxx +nobase_include_HEADERS = pqxx/pqxx \ + pqxx/array pqxx/array.hxx \ + pqxx/binarystring pqxx/binarystring.hxx \ + pqxx/blob pqxx/blob.hxx \ + pqxx/composite pqxx/composite.hxx \ + pqxx/connection pqxx/connection.hxx \ + pqxx/cursor pqxx/cursor.hxx \ + pqxx/dbtransaction pqxx/dbtransaction.hxx \ + pqxx/errorhandler pqxx/errorhandler.hxx \ + pqxx/except pqxx/except.hxx \ + pqxx/field pqxx/field.hxx \ + pqxx/isolation pqxx/isolation.hxx \ + pqxx/largeobject pqxx/largeobject.hxx \ + pqxx/nontransaction pqxx/nontransaction.hxx \ + pqxx/notification pqxx/notification.hxx \ + pqxx/params pqxx/params.hxx \ + pqxx/pipeline pqxx/pipeline.hxx \ + pqxx/prepared_statement pqxx/prepared_statement.hxx \ + pqxx/range pqxx/range.hxx \ + pqxx/result pqxx/result.hxx \ + pqxx/robusttransaction pqxx/robusttransaction.hxx \ + pqxx/separated_list pqxx/separated_list.hxx \ + pqxx/strconv pqxx/strconv.hxx \ + pqxx/stream_from pqxx/stream_from.hxx \ + pqxx/stream_to pqxx/stream_to.hxx \ + pqxx/subtransaction pqxx/subtransaction.hxx \ + pqxx/time pqxx/time.hxx \ + pqxx/transaction pqxx/transaction.hxx \ + pqxx/transaction_base pqxx/transaction_base.hxx \ + pqxx/transaction_focus pqxx/transaction_focus.hxx \ + pqxx/transactor pqxx/transactor.hxx \ + pqxx/row pqxx/row.hxx \ + pqxx/util pqxx/util.hxx \ + pqxx/types pqxx/types.hxx \ + pqxx/zview pqxx/zview.hxx \ + pqxx/version pqxx/version.hxx \ + pqxx/internal/array-composite.hxx \ + pqxx/internal/callgate.hxx \ + pqxx/internal/concat.hxx \ + pqxx/internal/conversions.hxx \ + pqxx/internal/encoding_group.hxx \ + pqxx/internal/encodings.hxx \ + pqxx/internal/header-pre.hxx \ + pqxx/internal/header-post.hxx \ + pqxx/internal/ignore-deprecated-post.hxx \ + pqxx/internal/ignore-deprecated-pre.hxx \ + pqxx/internal/libpq-forward.hxx \ + pqxx/internal/result_iter.hxx \ + pqxx/internal/result_iterator.hxx \ + pqxx/internal/sql_cursor.hxx \ + pqxx/internal/statement_parameters.hxx \ + pqxx/internal/stream_iterator.hxx \ + pqxx/internal/wait.hxx \ + pqxx/internal/gates/connection-errorhandler.hxx \ + pqxx/internal/gates/connection-largeobject.hxx \ + pqxx/internal/gates/connection-notification_receiver.hxx \ + pqxx/internal/gates/connection-pipeline.hxx \ + pqxx/internal/gates/connection-sql_cursor.hxx \ + pqxx/internal/gates/connection-transaction.hxx \ + pqxx/internal/gates/errorhandler-connection.hxx \ + pqxx/internal/gates/icursorstream-icursor_iterator.hxx \ + pqxx/internal/gates/icursor_iterator-icursorstream.hxx \ + pqxx/internal/gates/result-connection.hxx \ + pqxx/internal/gates/result-creation.hxx \ + pqxx/internal/gates/result-pipeline.hxx \ + pqxx/internal/gates/result-sql_cursor.hxx \ + pqxx/internal/gates/transaction-sql_cursor.hxx \ + pqxx/internal/gates/transaction-transaction_focus.hxx + +nobase_nodist_include_HEADERS = \ + pqxx/config-public-compiler.h + +EXTRA_DIST = \ + pqxx/doc/*.md \ + pqxx/doc/mainpage.md.template \ + pqxx/version.hxx.template + +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-nobase_includeHEADERS: $(nobase_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) +install-nobase_nodist_includeHEADERS: $(nobase_nodist_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_nodist_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(includedir)" "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-nobase_includeHEADERS \ + install-nobase_nodist_includeHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-nobase_includeHEADERS \ + uninstall-nobase_nodist_includeHEADERS + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-nobase_includeHEADERS \ + install-nobase_nodist_includeHEADERS install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-nobase_includeHEADERS \ + uninstall-nobase_nodist_includeHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ext/libpqxx-7.7.3/include/pqxx/Makefile.am b/ext/libpqxx-7.7.3/include/pqxx/Makefile.am new file mode 100644 index 000000000..c7f68813d --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/Makefile.am @@ -0,0 +1,13 @@ +MAINTAINERCLEANFILES=Makefile.in stamp-h.in + +noinst_HEADERS = \ + config-internal-autotools.h + +nodist_noinst_HEADERS = \ + config.h \ + config-internal-compiler.h + +DISTCLEANFILES = \ + config-internal-autotools.h \ + config-internal-compiler.h \ + config-public-compiler.h diff --git a/ext/libpqxx-7.7.3/include/pqxx/Makefile.in b/ext/libpqxx-7.7.3/include/pqxx/Makefile.in new file mode 100644 index 000000000..4b02fb771 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/Makefile.in @@ -0,0 +1,556 @@ +# Makefile.in generated by automake 1.16.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = include/pqxx +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/config/m4/libtool.m4 \ + $(top_srcdir)/config/m4/ltoptions.m4 \ + $(top_srcdir)/config/m4/ltsugar.m4 \ + $(top_srcdir)/config/m4/ltversion.m4 \ + $(top_srcdir)/config/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(nodist_noinst_HEADERS) $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ + config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ + $(top_srcdir)/config/mkinstalldirs +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PG_CONFIG = @PG_CONFIG@ +PKG_CONFIG = @PKG_CONFIG@ +POSTGRES_INCLUDE = @POSTGRES_INCLUDE@ +PQXXVERSION = @PQXXVERSION@ +PQXX_ABI = @PQXX_ABI@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_postgres_lib = @with_postgres_lib@ +MAINTAINERCLEANFILES = Makefile.in stamp-h.in +noinst_HEADERS = \ + config-internal-autotools.h + +nodist_noinst_HEADERS = \ + config.h \ + config-internal-compiler.h + +DISTCLEANFILES = \ + config-internal-autotools.h \ + config-internal-compiler.h \ + config-public-compiler.h + +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/pqxx/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/pqxx/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status include/pqxx/config.h +$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) config.h +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: all install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-hdr distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ext/libpqxx-7.7.3/include/pqxx/array b/ext/libpqxx-7.7.3/include/pqxx/array new file mode 100644 index 000000000..689f5b27b --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/array @@ -0,0 +1,6 @@ +/** Handling of SQL arrays. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/array.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/array.hxx b/ext/libpqxx-7.7.3/include/pqxx/array.hxx new file mode 100644 index 000000000..8440a244f --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/array.hxx @@ -0,0 +1,103 @@ +/* Handling of SQL arrays. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/field instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ARRAY +#define PQXX_H_ARRAY + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include + +#include "pqxx/internal/encoding_group.hxx" +#include "pqxx/internal/encodings.hxx" + + +namespace pqxx +{ +/// Low-level array parser. +/** Use this to read an array field retrieved from the database. + * + * This parser will only work reliably if your client encoding is UTF-8, ASCII, + * or a single-byte encoding which is a superset of ASCII (such as Latin-1). + * + * Also, the parser only supports array element types which use either a comma + * or a semicolon ("," or ";") as the separator between array elements. All + * built-in types use comma, except for one which uses semicolon, but some + * custom types may not work. + * + * The input is a C-style string containing the textual representation of an + * array, as returned by the database. The parser reads this representation + * on the fly. The string must remain in memory until parsing is done. + * + * Parse the array by making calls to @ref get_next until it returns a + * @ref juncture of "done". The @ref juncture tells you what the parser found + * in that step: did the array "nest" to a deeper level, or "un-nest" back up? + */ +class PQXX_LIBEXPORT array_parser +{ +public: + /// What's the latest thing found in the array? + enum class juncture + { + /// Starting a new row. + row_start, + /// Ending the current row. + row_end, + /// Found a NULL value. + null_value, + /// Found a string value. + string_value, + /// Parsing has completed. + done, + }; + + // TODO: constexpr noexcept. Breaks ABI. + /// Constructor. You don't need this; use @ref field::as_array instead. + /** The parser only remains valid while the data underlying the @ref result + * remains valid. Once all `result` objects referring to that data have been + * destroyed, the parser will no longer refer to valid memory. + */ + explicit array_parser( + std::string_view input, + internal::encoding_group = internal::encoding_group::MONOBYTE); + + /// Parse the next step in the array. + /** Returns what it found. If the juncture is @ref juncture::string_value, + * the string will contain the value. Otherwise, it will be empty. + * + * Call this until the @ref array_parser::juncture it returns is + * @ref juncture::done. + */ + std::pair get_next(); + +private: + std::string_view m_input; + internal::glyph_scanner_func *const m_scan; + + /// Current parsing position in the input. + std::string::size_type m_pos = 0u; + + std::string::size_type scan_single_quoted_string() const; + std::string parse_single_quoted_string(std::string::size_type end) const; + std::string::size_type scan_double_quoted_string() const; + std::string parse_double_quoted_string(std::string::size_type end) const; + std::string::size_type scan_unquoted_string() const; + std::string parse_unquoted_string(std::string::size_type end) const; + + std::string::size_type scan_glyph(std::string::size_type pos) const; + std::string::size_type + scan_glyph(std::string::size_type pos, std::string::size_type end) const; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/binarystring b/ext/libpqxx-7.7.3/include/pqxx/binarystring new file mode 100644 index 000000000..77551d9f7 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/binarystring @@ -0,0 +1,6 @@ +/** BYTEA (binary string) conversions. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/binarystring.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/binarystring.hxx b/ext/libpqxx-7.7.3/include/pqxx/binarystring.hxx new file mode 100644 index 000000000..47c82a035 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/binarystring.hxx @@ -0,0 +1,236 @@ +/* Deprecated representation for raw, binary data. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/binarystring instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_BINARYSTRING +#define PQXX_H_BINARYSTRING + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include + +#include "pqxx/result.hxx" +#include "pqxx/strconv.hxx" + +namespace pqxx +{ +class binarystring; +template<> struct string_traits; + + +/// Binary data corresponding to PostgreSQL's "BYTEA" binary-string type. +/** @ingroup escaping-functions + * @deprecated Use @c std::basic_string and + * @c std::basic_string_view for binary data. In C++20 or better, + * any @c contiguous_range of @c std::byte will do. + * + * This class represents a binary string as stored in a field of type @c bytea. + * + * Internally a binarystring is zero-terminated, but it may also contain null + * bytes, they're just like any other byte value. So don't assume that it's + * safe to treat the contents as a C-style string. + * + * The binarystring retains its value even if the result it was obtained from + * is destroyed, but it cannot be copied or assigned. + * + * \relatesalso transaction_base::quote_raw + * + * To include a @c binarystring value in an SQL query, escape and quote it + * using the transaction's @c quote_raw function. + * + * @warning This class is implemented as a reference-counting smart pointer. + * Copying, swapping, and destroying binarystring objects that refer to the + * same underlying data block is not thread-safe. If you wish to pass + * binarystrings around between threads, make sure that each of these + * operations is protected against concurrency with similar operations on the + * same object, or other objects pointing to the same data block. + */ +class PQXX_LIBEXPORT binarystring +{ +public: + using char_type = unsigned char; + using value_type = std::char_traits::char_type; + using size_type = std::size_t; + using difference_type = long; + using const_reference = value_type const &; + using const_pointer = value_type const *; + using const_iterator = const_pointer; + using const_reverse_iterator = std::reverse_iterator; + + [[deprecated("Use std::byte for binary data.")]] binarystring( + binarystring const &) = default; + + /// Read and unescape bytea field. + /** The field will be zero-terminated, even if the original bytea field + * isn't. + * @param F the field to read; must be a bytea field + */ + [[deprecated("Use std::byte for binary data.")]] explicit binarystring( + field const &); + + /// Copy binary data from std::string_view on binary data. + /** This is inefficient in that it copies the data to a buffer allocated on + * the heap. + */ + [[deprecated("Use std::byte for binary data.")]] explicit binarystring( + std::string_view); + + /// Copy binary data of given length straight out of memory. + [[deprecated("Use std::byte for binary data.")]] binarystring( + void const *, std::size_t); + + /// Efficiently wrap a buffer of binary data in a @c binarystring. + [[deprecated("Use std::byte for binary data.")]] binarystring( + std::shared_ptr ptr, size_type size) : + m_buf{std::move(ptr)}, m_size{size} + {} + + /// Size of converted string in bytes. + [[nodiscard]] size_type size() const noexcept { return m_size; } + /// Size of converted string in bytes. + [[nodiscard]] size_type length() const noexcept { return size(); } + [[nodiscard]] bool empty() const noexcept { return size() == 0; } + + [[nodiscard]] const_iterator begin() const noexcept { return data(); } + [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); } + [[nodiscard]] const_iterator end() const noexcept { return data() + m_size; } + [[nodiscard]] const_iterator cend() const noexcept { return end(); } + + [[nodiscard]] const_reference front() const noexcept { return *begin(); } + [[nodiscard]] const_reference back() const noexcept + { + return *(data() + m_size - 1); + } + + [[nodiscard]] const_reverse_iterator rbegin() const + { + return const_reverse_iterator{end()}; + } + [[nodiscard]] const_reverse_iterator crbegin() const { return rbegin(); } + [[nodiscard]] const_reverse_iterator rend() const + { + return const_reverse_iterator{begin()}; + } + [[nodiscard]] const_reverse_iterator crend() const { return rend(); } + + /// Unescaped field contents. + [[nodiscard]] value_type const *data() const noexcept { return m_buf.get(); } + + [[nodiscard]] const_reference operator[](size_type i) const noexcept + { + return data()[i]; + } + + [[nodiscard]] PQXX_PURE bool operator==(binarystring const &) const noexcept; + [[nodiscard]] bool operator!=(binarystring const &rhs) const noexcept + { + return not operator==(rhs); + } + + binarystring &operator=(binarystring const &); + + /// Index contained string, checking for valid index. + const_reference at(size_type) const; + + /// Swap contents with other binarystring. + void swap(binarystring &); + + /// Raw character buffer (no terminating zero is added). + /** @warning No terminating zero is added! If the binary data did not end in + * a null character, you will not find one here. + */ + [[nodiscard]] char const *get() const noexcept + { + return reinterpret_cast(m_buf.get()); + } + + /// Read contents as a std::string_view. + [[nodiscard]] std::string_view view() const noexcept + { + return std::string_view(get(), size()); + } + + /// Read as regular C++ string (may include null characters). + /** This creates and returns a new string object. Don't call this + * repeatedly; retrieve your string once and keep it in a local variable. + * Also, do not expect to be able to compare the string's address to that of + * an earlier invocation. + */ + [[nodiscard]] std::string str() const; + + /// Access data as a pointer to @c std::byte. + [[nodiscard]] std::byte const *bytes() const + { + return reinterpret_cast(get()); + } + + /// Read data as a @c std::basic_string_view. + [[nodiscard]] std::basic_string_view bytes_view() const + { + return std::basic_string_view{bytes(), size()}; + } + +private: + std::shared_ptr m_buf; + size_type m_size{0}; +}; + + +template<> struct nullness : no_null +{}; + + +/// String conversion traits for @c binarystring. +/** Defines the conversions between a @c binarystring and its PostgreSQL + * textual format, for communication with the database. + * + * These conversions rely on the "hex" format which was introduced in + * PostgreSQL 9.0. Both your libpq and the server must be recent enough to + * speak this format. + */ +template<> struct string_traits +{ + static std::size_t size_buffer(binarystring const &value) noexcept + { + return internal::size_esc_bin(std::size(value)); + } + + static zview to_buf(char *begin, char *end, binarystring const &value) + { + return generic_to_buf(begin, end, value); + } + + static char *into_buf(char *begin, char *end, binarystring const &value) + { + auto const budget{size_buffer(value)}; + if (internal::cmp_less(end - begin, budget)) + throw conversion_overrun{ + "Not enough buffer space to escape binary data."}; + std::string_view text{value.view()}; + internal::esc_bin(binary_cast(text), begin); + return begin + budget; + } + + static binarystring from_string(std::string_view text) + { + auto const size{pqxx::internal::size_unesc_bin(std::size(text))}; + std::shared_ptr buf{ + new unsigned char[size], [](unsigned char const *x) { delete[] x; }}; + pqxx::internal::unesc_bin(text, reinterpret_cast(buf.get())); +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return binarystring{std::move(buf), size}; +#include "pqxx/internal/ignore-deprecated-post.hxx" + } +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/blob b/ext/libpqxx-7.7.3/include/pqxx/blob new file mode 100644 index 000000000..3fd0afac9 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/blob @@ -0,0 +1,6 @@ +/** Binary Large Objects interface. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/blob.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/blob.hxx b/ext/libpqxx-7.7.3/include/pqxx/blob.hxx new file mode 100644 index 000000000..6d77be724 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/blob.hxx @@ -0,0 +1,351 @@ +/* Binary Large Objects interface. + * + * Read or write large objects, stored in their own storage on the server. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/largeobject instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_BLOB +#define PQXX_H_BLOB + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#if defined(PQXX_HAVE_PATH) +# include +#endif + +#if defined(PQXX_HAVE_RANGES) && __has_include() +# include +#endif + +#if defined(PQXX_HAVE_SPAN) && __has_include() +# include +#endif + +#include "pqxx/dbtransaction.hxx" + + +namespace pqxx +{ +/** Binary large object. + * + * This is how you store data that may be too large for the `BYTEA` type. + * Access operations are similar to those for a file: you can read, write, + * query or set the current reading/writing position, and so on. + * + * These large objects live in their own storage on the server, indexed by an + * integer object identifier ("oid"). + * + * Two `blob` objects may refer to the same actual large object in the + * database at the same time. Each will have its own reading/writing position, + * but writes to the one will of course affect what the other sees. + */ +class PQXX_LIBEXPORT blob +{ +public: + /// Create a new, empty large object. + /** You may optionally specify an oid for the new blob. If you do, then + * the new object will have that oid -- or creation will fail if there + * already is an object with that oid. + */ + [[nodiscard]] static oid create(dbtransaction &, oid = 0); + + /// Delete a large object, or fail if it does not exist. + static void remove(dbtransaction &, oid); + + /// Open blob for reading. Any attempt to write to it will fail. + [[nodiscard]] static blob open_r(dbtransaction &, oid); + // Open blob for writing. Any attempt to read from it will fail. + [[nodiscard]] static blob open_w(dbtransaction &, oid); + // Open blob for reading and/or writing. + [[nodiscard]] static blob open_rw(dbtransaction &, oid); + + /// You can default-construct a blob, but it won't do anything useful. + /** Most operations on a default-constructed blob will throw @ref + * usage_error. + */ + blob() = default; + + /// You can move a blob, but not copy it. The original becomes unusable. + blob(blob &&); + /// You can move a blob, but not copy it. The original becomes unusable. + blob &operator=(blob &&); + + blob(blob const &) = delete; + blob &operator=(blob const &) = delete; + ~blob(); + + /// Maximum number of bytes that can be read or written at a time. + /** The underlying protocol only supports reads and writes up to 2 GB + * exclusive. + * + * If you need to read or write more data to or from a binary large object, + * you'll have to break it up into chunks. + */ + static constexpr std::size_t chunk_limit = 0x7fffffff; + + /// Read up to `size` bytes of the object into `buf`. + /** Uses a buffer that you provide, resizing it as needed. If it suits you, + * this lets you allocate the buffer once and then re-use it multiple times. + * + * Resizes `buf` as needed. + * + * @warning The underlying protocol only supports reads up to 2GB at a time. + * If you need to read more, try making repeated calls to @ref append_to_buf. + */ + std::size_t read(std::basic_string &buf, std::size_t size); + +#if defined(PQXX_HAVE_SPAN) + /// Read up to `std::size(buf)` bytes from the object. + /** Retrieves bytes from the blob, at the current position, until `buf` is + * full or there are no more bytes to read, whichever comes first. + * + * Returns the filled portion of `buf`. This may be empty. + */ + template + std::span read(std::span buf) + { + return buf.subspan(0, raw_read(std::data(buf), std::size(buf))); + } +#endif // PQXX_HAVE_SPAN + +#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) + /// Read up to `std::size(buf)` bytes from the object. + /** Retrieves bytes from the blob, at the current position, until `buf` is + * full or there are no more bytes to read, whichever comes first. + * + * Returns the filled portion of `buf`. This may be empty. + */ + template std::span read(DATA &buf) + { + return {std::data(buf), raw_read(std::data(buf), std::size(buf))}; + } +#else // PQXX_HAVE_CONCEPTS && PQXX_HAVE_SPAN + /// Read up to `std::size(buf)` bytes from the object. + /** @deprecated As libpqxx moves to C++20 as its baseline language version, + * this will take and return `std::span`. + * + * Retrieves bytes from the blob, at the current position, until `buf` is + * full (i.e. its current size is reached), or there are no more bytes to + * read, whichever comes first. + * + * This function will not change either the size or the capacity of `buf`, + * only its contents. + * + * Returns the filled portion of `buf`. This may be empty. + */ + template + std::basic_string_view read(std::vector &buf) + { + return {std::data(buf), raw_read(std::data(buf), std::size(buf))}; + } +#endif // PQXX_HAVE_CONCEPTS && PQXX_HAVE_SPAN + +#if defined(PQXX_HAVE_CONCEPTS) + /// Write `data` to large object, at the current position. + /** If the writing position is at the end of the object, this will append + * `data` to the object's contents and move the writing position so that + * it's still at the end. + * + * If the writing position was not at the end, writing will overwrite the + * prior data, but it will not remove data that follows the part where you + * wrote your new data. + * + * @warning This is a big difference from writing to a file. You can + * overwrite some data in a large object, but this does not truncate the + * data that was already there. For example, if the object contained binary + * data "abc", and you write "12" at the starting position, the object will + * contain "12c". + * + * @warning The underlying protocol only supports writes up to 2 GB at a + * time. If you need to write more, try making repeated calls to + * @ref append_from_buf. + */ + template void write(DATA const &data) + { + raw_write(std::data(data), std::size(data)); + } +#else + /// Write `data` large object, at the current position. + /** If the writing position is at the end of the object, this will append + * `data` to the object's contents and move the writing position so that + * it's still at the end. + * + * If the writing position was not at the end, writing will overwrite the + * prior data, but it will not remove data that follows the part where you + * wrote your new data. + * + * @warning This is a big difference from writing to a file. You can + * overwrite some data in a large object, but this does not truncate the + * data that was already there. For example, if the object contained binary + * data "abc", and you write "12" at the starting position, the object will + * contain "12c". + * + * @warning The underlying protocol only supports writes up to 2 GB at a + * time. If you need to write more, try making repeated calls to + * @ref append_from_buf. + */ + template void write(DATA const &data) + { + raw_write(std::data(data), std::size(data)); + } +#endif + + /// Resize large object to `size` bytes. + /** If the blob is more than `size` bytes long, this removes the end so as + * to make the blob the desired length. + * + * If the blob is less than `size` bytes long, it adds enough zero bytes to + * make it the desired length. + */ + void resize(std::int64_t size); + + /// Return the current reading/writing position in the large object. + [[nodiscard]] std::int64_t tell() const; + + /// Set the current reading/writing position to an absolute offset. + /** Returns the new file offset. */ + std::int64_t seek_abs(std::int64_t offset = 0); + /// Move the current reading/writing position forwards by an offset. + /** To move backwards, pass a negative offset. + * + * Returns the new file offset. + */ + std::int64_t seek_rel(std::int64_t offset = 0); + /// Set the current position to an offset relative to the end of the blob. + /** You'll probably want an offset of zero or less. + * + * Returns the new file offset. + */ + std::int64_t seek_end(std::int64_t offset = 0); + + /// Create a binary large object containing given `data`. + /** You may optionally specify an oid for the new object. If you do, and an + * object with that oid already exists, creation will fail. + */ + static oid from_buf( + dbtransaction &tx, std::basic_string_view data, oid id = 0); + + /// Append `data` to binary large object. + /** The underlying protocol only supports appending blocks up to 2 GB. + */ + static void append_from_buf( + dbtransaction &tx, std::basic_string_view data, oid id); + + /// Read client-side file and store it server-side as a binary large object. + [[nodiscard]] static oid from_file(dbtransaction &, char const path[]); + +#if defined(PQXX_HAVE_PATH) && !defined(_WIN32) + /// Read client-side file and store it server-side as a binary large object. + /** This overload is not available on Windows, where `std::filesystem::path` + * converts to a `wchar_t` string rather than a `char` string. + */ + [[nodiscard]] static oid + from_file(dbtransaction &tx, std::filesystem::path const &path) + { + return from_file(tx, path.c_str()); + } +#endif + + /// Read client-side file and store it server-side as a binary large object. + /** In this version, you specify the binary large object's oid. If that oid + * is already in use, the operation will fail. + */ + static oid from_file(dbtransaction &, char const path[], oid); + +#if defined(PQXX_HAVE_PATH) && !defined(_WIN32) + /// Read client-side file and store it server-side as a binary large object. + /** In this version, you specify the binary large object's oid. If that oid + * is already in use, the operation will fail. + * + * This overload is not available on Windows, where `std::filesystem::path` + * converts to a `wchar_t` string rather than a `char` string. + */ + static oid + from_file(dbtransaction &tx, std::filesystem::path const &path, oid id) + { + return from_file(tx, path.c_str(), id); + } +#endif + + /// Convenience function: Read up to `max_size` bytes from blob with `id`. + /** You could easily do this yourself using the @ref open_r and @ref read + * functions, but it can save you a bit of code to do it this way. + */ + static void to_buf( + dbtransaction &, oid, std::basic_string &, + std::size_t max_size); + + /// Read part of the binary large object with `id`, and append it to `buf`. + /** Use this to break up a large read from one binary large object into one + * massive buffer. Just keep calling this function until it returns zero. + * + * The `offset` is how far into the large object your desired chunk is, and + * `append_max` says how much to try and read in one go. + */ + static std::size_t append_to_buf( + dbtransaction &tx, oid id, std::int64_t offset, + std::basic_string &buf, std::size_t append_max); + + /// Write a binary large object's contents to a client-side file. + static void to_file(dbtransaction &, oid, char const path[]); + +#if defined(PQXX_HAVE_PATH) && !defined(_WIN32) + /// Write a binary large object's contents to a client-side file. + /** This overload is not available on Windows, where `std::filesystem::path` + * converts to a `wchar_t` string rather than a `char` string. + */ + static void + to_file(dbtransaction &tx, oid id, std::filesystem::path const &path) + { + to_file(tx, id, path.c_str()); + } +#endif + + /// Close this blob. + /** This does not delete the blob from the database; it only terminates your + * local object for accessing the blob. + * + * Resets the blob to a useless state similar to one that was + * default-constructed. + * + * The destructor will do this for you automatically. Still, there is a + * reason to `close()` objects explicitly where possible: if an error should + * occur while closing, `close()` can throw an exception. A destructor + * cannot. + */ + void close(); + +private: + PQXX_PRIVATE blob(connection &conn, int fd) noexcept : + m_conn{&conn}, m_fd{fd} + {} + static PQXX_PRIVATE blob open_internal(dbtransaction &, oid, int); + static PQXX_PRIVATE pqxx::internal::pq::PGconn * + raw_conn(pqxx::connection *) noexcept; + static PQXX_PRIVATE pqxx::internal::pq::PGconn * + raw_conn(pqxx::dbtransaction const &) noexcept; + static PQXX_PRIVATE std::string errmsg(connection const *); + static PQXX_PRIVATE std::string errmsg(dbtransaction const &tx) + { + return errmsg(&tx.conn()); + } + PQXX_PRIVATE std::string errmsg() const { return errmsg(m_conn); } + PQXX_PRIVATE std::int64_t seek(std::int64_t offset, int whence); + std::size_t raw_read(std::byte buf[], std::size_t size); + void raw_write(std::byte const buf[], std::size_t size); + + connection *m_conn = nullptr; + int m_fd = -1; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/composite b/ext/libpqxx-7.7.3/include/pqxx/composite new file mode 100644 index 000000000..2bfa7ade9 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/composite @@ -0,0 +1,6 @@ +/** Handling of SQL "composite types." + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/composite.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/composite.hxx b/ext/libpqxx-7.7.3/include/pqxx/composite.hxx new file mode 100644 index 000000000..439b133a8 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/composite.hxx @@ -0,0 +1,149 @@ +#ifndef PQXX_H_COMPOSITE +#define PQXX_H_COMPOSITE + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/internal/array-composite.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/util.hxx" + +namespace pqxx +{ +/// Parse a string representation of a value of a composite type. +/** @warning This code is still experimental. Use with care. + * + * You may use this as a helper while implementing your own @ref string_traits + * for a composite type. + * + * This function interprets `text` as the string representation of a value of + * some composite type, and sets each of `fields` to the respective values of + * its fields. The field types must be copy-assignable. + * + * The number of fields must match the number of fields in the composite type, + * and there must not be any other text in the input. The function is meant to + * handle any value string that the backend can produce, but not necessarily + * every valid alternative spelling. + * + * Fields in composite types can be null. When this happens, the C++ type of + * the corresponding field reference must be of a type that can handle nulls. + * If you are working with a type that does not have an inherent null value, + * such as e.g. `int`, consider using `std::optional`. + */ +template +inline void parse_composite( + pqxx::internal::encoding_group enc, std::string_view text, T &...fields) +{ + static_assert(sizeof...(fields) > 0); + + auto const scan{pqxx::internal::get_glyph_scanner(enc)}; + auto const data{std::data(text)}; + auto const size{std::size(text)}; + if (size == 0) + throw conversion_error{"Cannot parse composite value from empty string."}; + + std::size_t here{0}, next{scan(data, size, here)}; + if (next != 1 or data[here] != '(') + throw conversion_error{ + internal::concat("Invalid composite value string: ", text)}; + + here = next; + + constexpr auto num_fields{sizeof...(fields)}; + std::size_t index{0}; + (pqxx::internal::parse_composite_field( + index, text, here, fields, scan, num_fields - 1), + ...); + if (here != std::size(text)) + throw conversion_error{internal::concat( + "Composite value did not end at the closing parenthesis: '", text, + "'.")}; + if (text[here - 1] != ')') + throw conversion_error{internal::concat( + "Composive value did not end in parenthesis: '", text, "'")}; +} + + +/// Parse a string representation of a value of a composite type. +/** @warning This version only works for UTF-8 and single-byte encodings. + * + * For proper encoding support, use the composite-type support in the + * `field` class. + */ +template +inline void parse_composite(std::string_view text, T &...fields) +{ + parse_composite(pqxx::internal::encoding_group::MONOBYTE, text, fields...); +} +} // namespace pqxx + + +namespace pqxx::internal +{ +constexpr char empty_composite_str[]{"()"}; +} // namespace pqxx::internal + + +namespace pqxx +{ +/// Estimate the buffer size needed to represent a value of a composite type. +/** Returns a conservative estimate. + */ +template +[[nodiscard]] inline std::size_t +composite_size_buffer(T const &...fields) noexcept +{ + constexpr auto num{sizeof...(fields)}; + + // Size for a multi-field composite includes room for... + // + opening parenthesis + // + field budgets + // + separating comma per field + // - comma after final field + // + closing parenthesis + // + terminating zero + + if constexpr (num == 0) + return std::size(pqxx::internal::empty_composite_str); + else + return 1 + (pqxx::internal::size_composite_field_buffer(fields) + ...) + + num + 1; +} + + +/// Render a series of values as a single composite SQL value. +/** @warning This code is still experimental. Use with care. + * + * You may use this as a helper while implementing your own `string_traits` + * for a composite type. + */ +template +inline char *composite_into_buf(char *begin, char *end, T const &...fields) +{ + if (std::size_t(end - begin) < composite_size_buffer(fields...)) + throw conversion_error{ + "Buffer space may not be enough to represent composite value."}; + + constexpr auto num_fields{sizeof...(fields)}; + if constexpr (num_fields == 0) + { + constexpr char empty[]{"()"}; + std::memcpy(begin, empty, std::size(empty)); + return begin + std::size(empty); + } + + char *pos{begin}; + *pos++ = '('; + + (pqxx::internal::write_composite_field(pos, end, fields), ...); + + // If we've got multiple fields, "backspace" that last comma. + if constexpr (num_fields > 1) + --pos; + *pos++ = ')'; + *pos++ = '\0'; + return pos; +} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/config.h.in b/ext/libpqxx-7.7.3/include/pqxx/config.h.in new file mode 100644 index 000000000..743579967 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/config.h.in @@ -0,0 +1,121 @@ +/* include/pqxx/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `pq' library (-lpq). */ +#undef HAVE_LIBPQ + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define if supports floating-point conversion. */ +#undef PQXX_HAVE_CHARCONV_FLOAT + +/* Define if supports integer conversion. */ +#undef PQXX_HAVE_CHARCONV_INT + +/* Define if compiler has C++20 std::cmp_greater etc. */ +#undef PQXX_HAVE_CMP + +/* Define if compiler supports Concepts and header. */ +#undef PQXX_HAVE_CONCEPTS + +/* Define if compiler supports __cxa_demangle */ +#undef PQXX_HAVE_CXA_DEMANGLE + +/* Define if g++ supports pure attribute */ +#undef PQXX_HAVE_GCC_PURE + +/* Define if g++ supports visibility attribute. */ +#undef PQXX_HAVE_GCC_VISIBILITY + +/* Define if likely & unlikely work. */ +#undef PQXX_HAVE_LIKELY + +/* Define if operator[] can take multiple arguments. */ +#undef PQXX_HAVE_MULTIDIMENSIONAL_SUBSCRIPT + +/* Define if compiler has usable std::filesystem::path. */ +#undef PQXX_HAVE_PATH + +/* Define if poll() is available. */ +#undef PQXX_HAVE_POLL + +/* Define if libpq has PQencryptPasswordConn (since pg 10). */ +#undef PQXX_HAVE_PQENCRYPTPASSWORDCONN + +/* Define if libpq has pipeline mode (since pg 14). */ +#undef PQXX_HAVE_PQ_PIPELINE + +/* Define if std::this_thread::sleep_for works. */ +#undef PQXX_HAVE_SLEEP_FOR + +/* Define if compiler has std::span. */ +#undef PQXX_HAVE_SPAN + +/* Define if strerror_r() is available. */ +#undef PQXX_HAVE_STRERROR_R + +/* Define if strerror_s() is available. */ +#undef PQXX_HAVE_STRERROR_S + +/* Define if thread_local is fully supported. */ +#undef PQXX_HAVE_THREAD_LOCAL + +/* Define if std::chrono has year_month_day etc. */ +#undef PQXX_HAVE_YEAR_MONTH_DAY + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION diff --git a/ext/libpqxx-7.7.3/include/pqxx/connection b/ext/libpqxx-7.7.3/include/pqxx/connection new file mode 100644 index 000000000..82ff43aa5 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/connection @@ -0,0 +1,8 @@ +/** pqxx::connection class. + * + * pqxx::connection encapsulates a connection to a database. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/connection.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/connection.hxx b/ext/libpqxx-7.7.3/include/pqxx/connection.hxx new file mode 100644 index 000000000..92454bb47 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/connection.hxx @@ -0,0 +1,1261 @@ +/* Definition of the connection class. + * + * pqxx::connection encapsulates a connection to a database. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/connection instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_CONNECTION +#define PQXX_H_CONNECTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Double-check in order to suppress an overzealous Visual C++ warning (#418). +#if defined(PQXX_HAVE_CONCEPTS) && __has_include() +# include +#endif + +#include "pqxx/errorhandler.hxx" +#include "pqxx/except.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/params.hxx" +#include "pqxx/separated_list.hxx" +#include "pqxx/strconv.hxx" +#include "pqxx/types.hxx" +#include "pqxx/util.hxx" +#include "pqxx/zview.hxx" + + +/** + * @addtogroup connections + * + * Use of the libpqxx library starts here. + * + * Everything that can be done with a database through libpqxx must go through + * a @ref pqxx::connection object. It connects to a database when you create + * it, and it terminates that communication during destruction. + * + * Many things come together in this class. Handling of error and warning + * messages, for example, is defined by @ref pqxx::errorhandler objects in the + * context of a connection. Prepared statements are also defined here. + * + * When you connect to a database, you pass a connection string containing any + * parameters and options, such as the server address and the database name. + * + * These are identical to the ones in libpq, the C language binding upon which + * libpqxx itself is built: + * + * https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING + * + * There are also environment variables you can set to provide defaults, again + * as defined by libpq: + * + * https://www.postgresql.org/docs/current/libpq-envars.html + * + * You can also create a database connection _asynchronously_ using an + * intermediate @ref pqxx::connecting object. + */ + +namespace pqxx::internal +{ +class sql_cursor; + +#if defined(PQXX_HAVE_CONCEPTS) +/// Concept: T is a range of pairs of zero-terminated strings. +template +concept ZKey_ZValues = std::ranges::input_range and requires(T t) +{ + {std::cbegin(t)}; + { + std::get<0>(*std::cbegin(t)) + } -> ZString; + { + std::get<1>(*std::cbegin(t)) + } -> ZString; +} and std::tuple_size_v::value_type> +== 2; +#endif // PQXX_HAVE_CONCEPTS +} // namespace pqxx::internal + + +namespace pqxx::internal::gate +{ +class connection_dbtransaction; +class connection_errorhandler; +class connection_largeobject; +class connection_notification_receiver; +class connection_pipeline; +class connection_sql_cursor; +class connection_stream_from; +class connection_stream_to; +class connection_transaction; +class const_connection_largeobject; +} // namespace pqxx::internal::gate + + +namespace pqxx +{ +/// Representation of a PostgreSQL table path. +/** A "table path" consists of a table name, optionally prefixed by a schema + * name, which in turn is optionally prefixed by a database name. + * + * A minimal example of a table path would be `{mytable}`. But a table path + * may also take the forms `{myschema,mytable}` or + * `{mydb,myschema,mytable}`. + */ +using table_path = std::initializer_list; + + +/// Encrypt a password. @deprecated Use connection::encrypt_password instead. +[[nodiscard, + deprecated("Use connection::encrypt_password instead.")]] std::string + PQXX_LIBEXPORT + encrypt_password(char const user[], char const password[]); + +/// Encrypt password. @deprecated Use connection::encrypt_password instead. +[[nodiscard, + deprecated("Use connection::encrypt_password instead.")]] inline std::string +encrypt_password(zview user, zview password) +{ +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return encrypt_password(user.c_str(), password.c_str()); +#include "pqxx/internal/ignore-deprecated-post.hxx" +} + + +/// Error verbosity levels. +enum class error_verbosity : int +{ + // These values must match those in libpq's PGVerbosity enum. + terse = 0, + normal = 1, + verbose = 2 +}; + + +/// Connection to a database. +/** This is the first class to look at when you wish to work with a database + * through libpqxx. The connection opens during construction, and closes upon + * destruction. + * + * When creating a connection, you can pass a connection URI or a postgres + * connection string, to specify the database server's address, a login + * username, and so on. If you don't, the connection will try to obtain them + * from certain environment variables. If those are not set either, the + * default is to try and connect to the local system's port 5432. + * + * Find more about connection strings here: + * + * https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING + * + * The variables are documented here: + * + * https://www.postgresql.org/docs/current/libpq-envars.html + * + * To query or manipulate the database once connected, use one of the + * transaction classes (see pqxx/transaction_base.hxx) and perhaps also the + * transactor framework (see pqxx/transactor.hxx). + * + * When a connection breaks, you will typically get a @ref broken_connection + * exception. This can happen at almost any point. + * + * @warning On Unix-like systems, including GNU and BSD systems, your program + * may receive the SIGPIPE signal when the connection to the backend breaks. By + * default this signal will abort your program. Use "signal(SIGPIPE, SIG_IGN)" + * if you want your program to continue running after a connection fails. + */ +class PQXX_LIBEXPORT connection +{ +public: + connection() : connection{""} {} + + /// Connect to a database, using `options` string. + explicit connection(char const options[]) + { + check_version(); + init(options); + } + + /// Connect to a database, using `options` string. + explicit connection(zview options) : connection{options.c_str()} + { + // (Delegates to other constructor which calls check_version for us.) + } + + /// Move constructor. + /** Moving a connection is not allowed if it has an open transaction, or has + * error handlers or notification receivers registered on it. In those + * situations, other objects may hold references to the old object which + * would become invalid and might produce hard-to-diagnose bugs. + */ + connection(connection &&rhs); + +#if defined(PQXX_HAVE_CONCEPTS) + /// Connect to a database, passing options as a range of key/value pairs. + /** @warning Experimental. Requires C++20 "concepts" support. Define + * `PQXX_HAVE_CONCEPTS` to enable it. + * + * There's no need to escape the parameter values. + * + * See the PostgreSQL libpq documentation for the full list of possible + * options: + * + * https://postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS + * + * The options can be anything that can be iterated as a series of pairs of + * zero-terminated strings: `std::pair`, or + * `std::tuple`, or + * `std::map`, and so on. + */ + template + inline connection(MAPPING const ¶ms); +#endif // PQXX_HAVE_CONCEPTS + + ~connection() + { + try + { + close(); + } + catch (std::exception const &) + {} + } + + /// Move assignment. + /** Neither connection can have an open transaction, registered error + * handlers, or registered notification receivers. + */ + connection &operator=(connection &&rhs); + + connection(connection const &) = delete; + connection &operator=(connection const &) = delete; + + /// Is this connection open at the moment? + /** @warning This function is **not** needed in most code. Resist the + * temptation to check it after opening a connection. The `connection` + * constructor will throw a @ref broken_connection exception if can't connect + * to the database. + */ + [[nodiscard]] bool PQXX_PURE is_open() const noexcept; + + /// Invoke notice processor function. The message should end in newline. + void process_notice(char const[]) noexcept; + /// Invoke notice processor function. Newline at end is recommended. + /** The zview variant, with a message ending in newline, is the most + * efficient way to call process_notice. + */ + void process_notice(zview) noexcept; + + /// Enable tracing to a given output stream, or nullptr to disable. + void trace(std::FILE *) noexcept; + + /** + * @name Connection properties + * + * These are probably not of great interest, since most are derived from + * information supplied by the client program itself, but they are included + * for completeness. + * + * The connection needs to be currently active for these to work. + */ + //@{ + /// Name of database we're connected to, if any. + [[nodiscard]] char const *dbname() const; + + /// Database user ID we're connected under, if any. + [[nodiscard]] char const *username() const; + + /// Address of server, or nullptr if none specified (i.e. default or local) + [[nodiscard]] char const *hostname() const; + + /// Server port number we're connected to. + [[nodiscard]] char const *port() const; + + /// Process ID for backend process, or 0 if inactive. + [[nodiscard]] int PQXX_PURE backendpid() const &noexcept; + + /// Socket currently used for connection, or -1 for none. Use with care! + /** Query the current socket number. This is intended for event loops based + * on functions such as select() or poll(), where you're waiting for any of + * multiple file descriptors to become ready for communication. + * + * Please try to stay away from this function. It is really only meant for + * event loops that need to wait on more than one file descriptor. If all + * you need is to block until a notification arrives, for instance, use + * await_notification(). If you want to issue queries and retrieve results + * in nonblocking fashion, check out the pipeline class. + */ + [[nodiscard]] int PQXX_PURE sock() const &noexcept; + + /// What version of the PostgreSQL protocol is this connection using? + /** The answer can be 0 (when there is no connection); 3 for protocol 3.0; or + * possibly higher values as newer protocol versions come into use. + */ + [[nodiscard]] int PQXX_PURE protocol_version() const noexcept; + + /// What version of the PostgreSQL server are we connected to? + /** The result is a bit complicated: each of the major, medium, and minor + * release numbers is written as a two-digit decimal number, and the three + * are then concatenated. Thus server version 9.4.2 will be returned as the + * decimal number 90402. If there is no connection to the server, this + * returns zero. + * + * @warning When writing version numbers in your code, don't add zero at the + * beginning! Numbers beginning with zero are interpreted as octal (base-8) + * in C++. Thus, 070402 is not the same as 70402, and 080000 is not a number + * at all because there is no digit "8" in octal notation. Use strictly + * decimal notation when it comes to these version numbers. + */ + [[nodiscard]] int PQXX_PURE server_version() const noexcept; + //@} + + /// @name Text encoding + /** + * Each connection is governed by a "client encoding," which dictates how + * strings and other text is represented in bytes. The database server will + * send text data to you in this encoding, and you should use it for the + * queries and data which you send to the server. + * + * Search the PostgreSQL documentation for "character set encodings" to find + * out more about the available encodings, how to extend them, and how to use + * them. Not all server-side encodings are compatible with all client-side + * encodings or vice versa. + * + * Encoding names are case-insensitive, so e.g. "UTF8" is equivalent to + * "utf8". + * + * You can change the client encoding, but this may not work when the + * connection is in a special state, such as when streaming a table. It's + * not clear what happens if you change the encoding during a transaction, + * and then abort the transaction. + */ + //@{ + /// Get client-side character encoding, by name. + [[nodiscard]] std::string get_client_encoding() const; + + /// Set client-side character encoding, by name. + /** + * @param encoding Name of the character set encoding to use. + */ + void set_client_encoding(zview encoding) & + { + set_client_encoding(encoding.c_str()); + } + + /// Set client-side character encoding, by name. + /** + * @param encoding Name of the character set encoding to use. + */ + void set_client_encoding(char const encoding[]) &; + + /// Get the connection's encoding, as a PostgreSQL-defined code. + [[nodiscard]] int PQXX_PRIVATE encoding_id() const; + + //@} + + /// Set session variable, using SQL's `SET` command. + /** @deprecated To set a session variable, use @ref set_session_var. To set + * a transaction-local variable, execute an SQL `SET` command. + * + * @warning When setting a string value, you must escape and quote it first. + * Use the @ref quote() function to do that. + * + * @warning This executes an SQL query, so do not get or set variables while + * a table stream or pipeline is active on the same connection. + * + * @param var Variable to set. + * @param value New value for Var. This can be any SQL expression. If it's + * a string, be sure that it's properly escaped and quoted. + */ + [[deprecated("To set session variables, use set_session_var.")]] void + set_variable(std::string_view var, std::string_view value) &; + + /// Set one of the session variables to a new value. + /** This executes SQL, so do not do it while a pipeline or stream is active + * on the connection. + * + * The value you set here will last for the rest of the connection's + * duration, or until you set a new value. + * + * If you set the value while in a @ref dbtransaction (i.e. any transaction + * that is not a @ref nontransaction), then rolling back the transaction will + * undo the change. + * + * All applies to setting _session_ variables. You can also set the same + * variables as _local_ variables, in which case they will always revert to + * their previous value when the transaction ends (or when you overwrite them + * of course). To set a local variable, simply execute an SQL statement + * along the lines of "`SET LOCAL var = 'value'`" inside your transaction. + * + * @param var The variable to set. + * @param value The new value for the variable. + * @throw @ref variable_set_to_null if the value is null; this is not + * allowed. + */ + template + void set_session_var(std::string_view var, TYPE const &value) & + { + if constexpr (nullness::has_null) + { + if (nullness::is_null(value)) + throw variable_set_to_null{ + internal::concat("Attempted to set variable ", var, " to null.")}; + } + exec(internal::concat("SET ", quote_name(var), "=", quote(value))); + } + + /// Read session variable, using SQL's `SHOW` command. + /** @warning This executes an SQL query, so do not get or set variables while + * a table stream or pipeline is active on the same connection. + */ + [[deprecated("Use get_var instead.")]] std::string + get_variable(std::string_view); + + /// Read currently applicable value of a variable. + /** This function executes an SQL statement, so it won't work while a + * @ref pipeline or query stream is active on the connection. + * + * @return a blank `std::optional` if the variable's value is null, or its + * string value otherwise. + */ + std::string get_var(std::string_view var); + + /// Read currently applicable value of a variable. + /** This function executes an SQL statement, so it won't work while a + * @ref pipeline or query stream is active on the connection. + * + * If there is any possibility that the variable is null, ensure that `TYPE` + * can represent null values. + */ + template TYPE get_var_as(std::string_view var) + { + return from_string(get_var(var)); + } + + /** + * @name Notifications and Receivers + */ + //@{ + /// Check for pending notifications and take appropriate action. + /** This does not block. To wait for incoming notifications, either call + * await_notification() (it calls this function); or wait for incoming data + * on the connection's socket (i.e. wait to read), and then call this + * function repeatedly until it returns zero. After that, there are no more + * pending notifications so you may want to wait again. + * + * If any notifications are pending when you call this function, it + * processes them by finding any receivers that match the notification string + * and invoking those. If no receivers match, there is nothing to invoke but + * we do consider the notification processed. + * + * If any of the client-registered receivers throws an exception, the + * function will report it using the connection's errorhandlers. It does not + * re-throw the exceptions. + * + * @return Number of notifications processed. + */ + int get_notifs(); + + /// Wait for a notification to come in. + /** There are other events that will also terminate the wait, such as the + * backend failing. It will also wake up periodically. + * + * If a notification comes in, the call will process it, along with any other + * notifications that may have been pending. + * + * To wait for notifications into your own event loop instead, wait until + * there is incoming data on the connection's socket to be read, then call + * @ref get_notifs() repeatedly until it returns zero. + * + * @return Number of notifications processed. + */ + int await_notification(); + + /// Wait for a notification to come in, or for given timeout to pass. + /** There are other events that will also terminate the wait, such as the + * backend failing, or timeout expiring. + * + * If a notification comes in, the call will process it, along with any other + * notifications that may have been pending. + * + * To wait for notifications into your own event loop instead, wait until + * there is incoming data on the connection's socket to be read, then call + * @ref get_notifs repeatedly until it returns zero. + * + * @return Number of notifications processed + */ + int await_notification(std::time_t seconds, long microseconds); + //@} + + /** + * @name Password encryption + * + * Use this when setting a new password for the user if password encryption + * is enabled. Inputs are the SQL name for the user for whom you with to + * encrypt a password; the plaintext password; and the hash algorithm. + * + * The algorithm must be one of "md5", "scram-sha-256" (introduced in + * PostgreSQL 10), or `nullptr`. If the pointer is null, this will query + * the `password_encryption setting` from the server, and use the default + * algorithm as defined there. + * + * @return encrypted version of the password, suitable for encrypted + * PostgreSQL authentication. + * + * Thus you can change a user's password with: + * ```cxx + * void setpw(transaction_base &t, string const &user, string const &pw) + * { + * t.exec0("ALTER USER " + user + " " + * "PASSWORD '" + t.conn().encrypt_password(user,pw) + "'"); + * } + * ``` + * + * When building this against a libpq older than version 10, this will use + * an older function which only supports md5. In that case, requesting a + * different algorithm than md5 will result in a @ref feature_not_supported + * exception. + */ + //@{ + /// Encrypt a password for a given user. + [[nodiscard]] std::string + encrypt_password(zview user, zview password, zview algorithm) + { + return encrypt_password(user.c_str(), password.c_str(), algorithm.c_str()); + } + /// Encrypt a password for a given user. + [[nodiscard]] std::string encrypt_password( + char const user[], char const password[], char const *algorithm = nullptr); + //@} + + /** + * @name Prepared statements + * + * PostgreSQL supports prepared SQL statements, i.e. statements that you can + * register under a name you choose, optimized once by the backend, and + * executed any number of times under the given name. + * + * Prepared statement definitions are not sensitive to transaction + * boundaries. A statement defined inside a transaction will remain defined + * outside that transaction, even if the transaction itself is subsequently + * aborted. Once a statement has been prepared, it will only go away if you + * close the connection or explicitly "unprepare" the statement. + * + * Use the `pqxx::transaction_base::exec_prepared` functions to execute a + * prepared statement. See @ref prepared for a full discussion. + * + * @warning Using prepared statements can save time, but if your statement + * takes parameters, it may also make your application significantly slower! + * The reason is that the server works out a plan for executing the query + * when you prepare it. At that time, of course it does not know the values + * for the parameters that you will pass. If you execute a query without + * preparing it, then the server works out the plan on the spot, with full + * knowledge of the parameter values. + * + * A statement's definition can refer to its parameters as `$1`, `$2`, etc. + * The first parameter you pass to the call provides a value for `$1`, and + * so on. + * + * Here's an example of how to use prepared statements. + * + * ```cxx + * using namespace pqxx; + * void foo(connection &c) + * { + * c.prepare("findtable", "select * from pg_tables where name=$1"); + * work tx{c}; + * result r = tx.exec_prepared("findtable", "mytable"); + * if (std::empty(r)) throw runtime_error{"mytable not found!"}; + * } + * ``` + */ + //@{ + + /// Define a prepared statement. + /** + * @param name unique name for the new prepared statement. + * @param definition SQL statement to prepare. + */ + void prepare(zview name, zview definition) & + { + prepare(name.c_str(), definition.c_str()); + } + + /** + * @param name unique name for the new prepared statement. + * @param definition SQL statement to prepare. + */ + void prepare(char const name[], char const definition[]) &; + + /// Define a nameless prepared statement. + /** + * This can be useful if you merely want to pass large binary parameters to a + * statement without otherwise wishing to prepare it. If you use this + * feature, always keep the definition and the use close together to avoid + * the nameless statement being redefined unexpectedly by code somewhere + * else. + */ + void prepare(char const definition[]) &; + void prepare(zview definition) & { return prepare(definition.c_str()); } + + /// Drop prepared statement. + void unprepare(std::string_view name); + + //@} + + // C++20: constexpr. Breaks ABI. + /// Suffix unique number to name to make it unique within session context. + /** Used internally to generate identifiers for SQL objects (such as cursors + * and nested transactions) based on a given human-readable base name. + */ + [[nodiscard]] std::string adorn_name(std::string_view); + + /** + * @defgroup escaping-functions String-escaping functions + */ + //@{ + + /// Escape string for use as SQL string literal on this connection. + /** @warning This accepts a length, and it does not require a terminating + * zero byte. But if there is a zero byte, escaping stops there even if + * it's not at the end of the string! + */ + [[deprecated("Use std::string_view or pqxx:zview.")]] std::string + esc(char const text[], std::size_t maxlen) const + { + return esc(std::string_view{text, maxlen}); + } + + /// Escape string for use as SQL string literal on this connection. + [[nodiscard]] std::string esc(char const text[]) const + { + return esc(std::string_view{text}); + } + +#if defined(PQXX_HAVE_SPAN) + /// Escape string for use as SQL string literal, into `buffer`. + /** Use this variant when you want to re-use the same buffer across multiple + * calls. If that's not the case, or convenience and simplicity are more + * important, use the single-argument variant. + * + * For every byte in `text`, there must be at least 2 bytes of space in + * `buffer`; plus there must be one byte of space for a trailing zero. + * Throws @ref range_error if this space is not available. + * + * Returns a reference to the escaped string, which is actually stored in + * `buffer`. + */ + [[nodiscard]] std::string_view + esc(std::string_view text, std::span buffer) + { + auto const size{std::size(text)}, space{std::size(buffer)}; + auto const needed{2 * size + 1}; + if (space < needed) + throw range_error{internal::concat( + "Not enough room to escape string of ", size, " byte(s): need ", + needed, " bytes of buffer space, but buffer size is ", space, ".")}; + auto const data{buffer.data()}; + return {data, esc_to_buf(text, data)}; + } +#endif + + /// Escape string for use as SQL string literal on this connection. + /** @warning This is meant for text strings only. It cannot contain bytes + * whose value is zero ("nul bytes"). + */ + [[nodiscard]] std::string esc(std::string_view text) const; + +#if defined(PQXX_HAVE_CONCEPTS) + /// Escape binary string for use as SQL string literal on this connection. + /** This is identical to `esc_raw(data)`. */ + template [[nodiscard]] std::string esc(DATA const &data) const + { + return esc_raw(data); + } +#endif + +#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) + /// Escape binary string for use as SQL string literal, into `buffer`. + /** Use this variant when you want to re-use the same buffer across multiple + * calls. If that's not the case, or convenience and simplicity are more + * important, use the single-argument variant. + * + * For every byte in `data`, there must be at least two bytes of space in + * `buffer`; plus there must be two bytes of space for a header and one for + * a trailing zero. Throws @ref range_error if this space is not available. + * + * Returns a reference to the escaped string, which is actually stored in + * `buffer`. + */ + template + [[nodiscard]] zview esc(DATA const &data, std::span buffer) const + { + auto const size{std::size(data)}, space{std::size(buffer)}; + auto const needed{internal::size_esc_bin(std::size(data))}; + if (space < needed) + throw range_error{internal::concat( + "Not enough room to escape binary string of ", size, " byte(s): need ", + needed, " bytes of buffer space, but buffer size is ", space, ".")}; + + std::basic_string_view view{std::data(data), std::size(data)}; + auto const out{std::data(buffer)}; + // Actually, in the modern format, we know beforehand exactly how many + // bytes we're going to fill. Just leave out the trailing zero. + internal::esc_bin(view, out); + return zview{out, needed - 1}; + } +#endif + + /// Escape binary string for use as SQL string literal on this connection. + [[deprecated("Use std::byte for binary data.")]] std::string + esc_raw(unsigned char const bin[], std::size_t len) const; + + /// Escape binary string for use as SQL string literal on this connection. + /** You can also just use @ref esc with a binary string. */ + [[nodiscard]] std::string esc_raw(std::basic_string_view) const; + +#if defined(PQXX_HAVE_SPAN) + /// Escape binary string for use as SQL string literal, into `buffer`. + /** You can also just use @ref esc with a binary string. */ + [[nodiscard]] std::string + esc_raw(std::basic_string_view, std::span buffer) const; +#endif + +#if defined(PQXX_HAVE_CONCEPTS) + /// Escape binary string for use as SQL string literal on this connection. + /** You can also just use @ref esc with a binary string. */ + template + [[nodiscard]] std::string esc_raw(DATA const &data) const + { + return esc_raw( + std::basic_string_view{std::data(data), std::size(data)}); + } +#endif + +#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) + /// Escape binary string for use as SQL string literal, into `buffer`. + template + [[nodiscard]] zview esc_raw(DATA const &data, std::span buffer) const + { + return this->esc(binary_cast(data), buffer); + } +#endif + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string + unesc_raw(zview text) const + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return unesc_raw(text.c_str()); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string + unesc_raw(char const text[]) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + * + * (The data must be encoded in PostgreSQL's "hex" format. The legacy + * "bytea" escape format, used prior to PostgreSQL 9.0, is no longer + * supported.) + */ + [[nodiscard]] std::basic_string + unesc_bin(std::string_view text) const + { + std::basic_string buf; + buf.resize(pqxx::internal::size_unesc_bin(std::size(text))); + pqxx::internal::unesc_bin(text, buf.data()); + return buf; + } + + /// Escape and quote a string of binary data. + [[deprecated("Use quote(std::basic_string_view).")]] std::string + quote_raw(unsigned char const bin[], std::size_t len) const; + + /// Escape and quote a string of binary data. + std::string quote_raw(std::basic_string_view) const; + +#if defined(PQXX_HAVE_CONCEPTS) + /// Escape and quote a string of binary data. + /** You can also just use @ref quote with binary data. */ + template + [[nodiscard]] std::string quote_raw(DATA const &data) const + { + return quote_raw( + std::basic_string_view{std::data(data), std::size(data)}); + } +#endif + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Escape and quote an SQL identifier for use in a query. + [[nodiscard]] std::string quote_name(std::string_view identifier) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Escape and quote a table name. + /** When passing just a table name, this is just another name for + * @ref quote_name. + */ + [[nodiscard]] std::string quote_table(std::string_view name) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Escape and quote a table path. + /** A table path consists of a table name, optionally prefixed by a schema + * name; and if both are given, they are in turn optionally prefixed by a + * database name. + * + * Each portion of the path (database name, schema name, table name) will be + * quoted separately, and they will be joined together by dots. So for + * example, `myschema.mytable` will become `"myschema"."mytable"`. + */ + [[nodiscard]] std::string quote_table(table_path) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Quote and comma-separate a series of column names. + /** Use this to save a bit of work in cases where you repeatedly need to pass + * the same list of column names, e.g. with @ref stream_to and @ref + * stream_from. Some functions that need to quote the columns list + * internally, will have a "raw" alternative which let you do the quoting + * yourself. It's a bit of extra work, but it can in rare cases let you + * eliminate some duplicate work in quoting them repeatedly. + */ + template + inline std::string quote_columns(STRINGS const &columns) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Represent object as SQL string, including quoting & escaping. + /** + * Recognises nulls and represents them as SQL nulls. They get no quotes. + */ + template + [[nodiscard]] inline std::string quote(T const &t) const; + + [[deprecated("Use std::byte for binary data.")]] std::string + quote(binarystring const &) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Escape and quote binary data for use as a BYTEA value in SQL statement. + [[nodiscard]] std::string + quote(std::basic_string_view bytes) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Escape string for literal LIKE match. + /** Use this when part of an SQL "LIKE" pattern should match only as a + * literal string, not as a pattern, even if it contains "%" or "_" + * characters that would normally act as wildcards. + * + * The string does not get string-escaped or quoted. You do that later. + * + * For instance, let's say you have a string `name` entered by the user, + * and you're searching a `file` column for items that match `name` + * followed by a dot and three letters. Even if `name` contains wildcard + * characters "%" or "_", you only want those to match literally, so "_" + * only matches "_" and "%" only matches a single "%". + * + * You do that by "like-escaping" `name`, appending the wildcard pattern + * `".___"`, and finally, escaping and quoting the result for inclusion in + * your query: + * + * ```cxx + * tx.exec( + * "SELECT file FROM item WHERE file LIKE " + + * tx.quote(tx.esc_like(name) + ".___")); + * ``` + * + * The SQL "LIKE" operator also lets you choose your own escape character. + * This is supported, but must be a single-byte character. + */ + [[nodiscard]] std::string + esc_like(std::string_view text, char escape_char = '\\') const; + //@} + + /// Attempt to cancel the ongoing query, if any. + /** You can use this from another thread, and/or while a query is executing + * in a pipeline, but it's up to you to ensure that you're not canceling the + * wrong query. This may involve locking. + */ + void cancel_query(); + +#if defined(_WIN32) || __has_include() + /// Set socket to blocking (true) or nonblocking (false). + /** @warning Do not use this unless you _really_ know what you're doing. + * @warning This function is available on most systems, but not necessarily + * all. + */ + void set_blocking(bool block) &; +#endif // defined(_WIN32) || __has_include() + + /// Set session verbosity. + /** Set the verbosity of error messages to "terse", "normal" (the default), + * or "verbose." + * + * If "terse", returned messages include severity, primary text, and + * position only; this will normally fit on a single line. "normal" produces + * messages that include the above plus any detail, hint, or context fields + * (these might span multiple lines). "verbose" includes all available + * fields. + */ + void set_verbosity(error_verbosity verbosity) &noexcept; + + /// Return pointers to the active errorhandlers. + /** The entries are ordered from oldest to newest handler. + * + * You may use this to find errorhandlers that your application wants to + * delete when destroying the connection. Be aware, however, that libpqxx + * may also add errorhandlers of its own, and those will be included in the + * list. If this is a problem for you, derive your errorhandlers from a + * custom base class derived from pqxx::errorhandler. Then use dynamic_cast + * to find which of the error handlers are yours. + * + * The pointers point to the real errorhandlers. The container it returns + * however is a copy of the one internal to the connection, not a reference. + */ + [[nodiscard]] std::vector get_errorhandlers() const; + + /// Return a connection string encapsulating this connection's options. + /** The connection must be currently open for this to work. + * + * Returns a reconstruction of this connection's connection string. It may + * not exactly match the connection string you passed in when creating this + * connection. + */ + [[nodiscard]] std::string connection_string() const; + + /// Explicitly close the connection. + /** The destructor will do this for you automatically. Still, there is a + * reason to `close()` objects explicitly where possible: if an error should + * occur while closing, `close()` can throw an exception. A destructor + * cannot. + * + * Closing a connection is idempotent. Closing a connection that's already + * closed does nothing. + */ + void close(); + + /// Seize control of a raw libpq connection. + /** @warning Do not do this. Please. It's for very rare, very specific + * use-cases. The mechanism may change (or break) in unexpected ways in + * future versions. + * + * @param raw_conn a raw libpq `PQconn` pointer. + */ + static connection seize_raw_connection(internal::pq::PGconn *raw_conn) + { + return connection{raw_conn}; + } + + /// Release the raw connection without closing it. + /** @warning Do not do this. It's for very rare, very specific use-cases. + * The mechanism may change (or break) in unexpected ways in future versions. + * + * The `connection` object becomes unusable after this. + */ + internal::pq::PGconn *release_raw_connection() && + { + return std::exchange(m_conn, nullptr); + } + +private: + friend class connecting; + enum connect_mode + { + connect_nonblocking + }; + connection(connect_mode, zview connection_string); + + /// For use by @ref seize_raw_connection. + explicit connection(internal::pq::PGconn *raw_conn) : m_conn{raw_conn} {} + + /// Poll for ongoing connection, try to progress towards completion. + /** Returns a pair of "now please wait to read data from socket" and "now + * please wait to write data to socket." Both will be false when done. + * + * Throws an exception if polling indicates that the connection has failed. + */ + std::pair poll_connect(); + + // Initialise based on connection string. + void init(char const options[]); + // Initialise based on parameter names and values. + void init(char const *params[], char const *values[]); + void complete_init(); + + result make_result( + internal::pq::PGresult *pgr, std::shared_ptr const &query, + std::string_view desc = ""sv); + + void PQXX_PRIVATE set_up_state(); + + int PQXX_PRIVATE PQXX_PURE status() const noexcept; + + /// Escape a string, into a buffer allocated by the caller. + /** The buffer must have room for at least `2*std::size(text) + 1` bytes. + * + * Returns the number of bytes written, including the trailing zero. + */ + std::size_t esc_to_buf(std::string_view text, char *buf) const; + + friend class internal::gate::const_connection_largeobject; + char const *PQXX_PURE err_msg() const noexcept; + + void PQXX_PRIVATE process_notice_raw(char const msg[]) noexcept; + + result exec_prepared(std::string_view statement, internal::c_params const &); + + /// Throw @ref usage_error if this connection is not in a movable state. + void check_movable() const; + /// Throw @ref usage_error if not in a state where it can be move-assigned. + void check_overwritable() const; + + friend class internal::gate::connection_errorhandler; + void PQXX_PRIVATE register_errorhandler(errorhandler *); + void PQXX_PRIVATE unregister_errorhandler(errorhandler *) noexcept; + + friend class internal::gate::connection_transaction; + result exec(std::string_view, std::string_view = ""sv); + result + PQXX_PRIVATE exec(std::shared_ptr, std::string_view = ""sv); + void PQXX_PRIVATE register_transaction(transaction_base *); + void PQXX_PRIVATE unregister_transaction(transaction_base *) noexcept; + + friend class internal::gate::connection_stream_from; + std::pair>, std::size_t> + PQXX_PRIVATE read_copy_line(); + + friend class internal::gate::connection_stream_to; + void PQXX_PRIVATE write_copy_line(std::string_view); + void PQXX_PRIVATE end_copy_write(); + + friend class internal::gate::connection_largeobject; + internal::pq::PGconn *raw_connection() const { return m_conn; } + + friend class internal::gate::connection_notification_receiver; + void add_receiver(notification_receiver *); + void remove_receiver(notification_receiver *) noexcept; + + friend class internal::gate::connection_pipeline; + void PQXX_PRIVATE start_exec(char const query[]); + bool PQXX_PRIVATE consume_input() noexcept; + bool PQXX_PRIVATE is_busy() const noexcept; + internal::pq::PGresult *get_result(); + + friend class internal::gate::connection_dbtransaction; + friend class internal::gate::connection_sql_cursor; + + result exec_params(std::string_view query, internal::c_params const &args); + + /// Connection handle. + internal::pq::PGconn *m_conn = nullptr; + + /// Active transaction on connection, if any. + /** We don't use this for anything, except to check for open transactions + * when we close the connection or start a new transaction. + * + * We also don't allow move construction or move assignment while there's a + * transaction, since moving the connection in that case would leave one or + * more pointers back from the transaction to the connection dangling. + */ + transaction_base const *m_trans = nullptr; + + std::list m_errorhandlers; + + using receiver_list = + std::multimap; + /// Notification receivers. + receiver_list m_receivers; + + /// Unique number to use as suffix for identifiers (see adorn_name()). + int m_unique_id = 0; +}; + + +/// @deprecated Old base class for connection. They are now the same class. +using connection_base = connection; + + +/// An ongoing, non-blocking stepping stone to a connection. +/** Use this when you want to create a connection to the database, but without + * blocking your whole thread. It is only available on systems that have + * the `` header, and Windows. + * + * Connecting in this way is probably not "faster" (it's more complicated and + * has some extra overhead), but in some situations you can use it to make your + * application as a whole faster. It all depends on having other useful work + * to do in the same thread, and being able to wait on a socket. If you have + * other I/O going on at the same time, your event loop can wait for both the + * libpqxx socket and your own sockets, and wake up whenever any of them is + * ready to do work. + * + * Connecting in this way is not properly "asynchronous;" it's merely + * "nonblocking." This means it's not a super-high-performance mechanism like + * you might get with e.g. `io_uring`. In particular, if we need to look up + * the database hostname in DNS, that will happen synchronously. + * + * To use this, create the `connecting` object, passing a connection string. + * Then loop: If @ref wait_to_read returns true, wait for the socket to have + * incoming data on it. If @ref wait_to_write returns true, wait for the + * socket to be ready for writing. Then call @ref process to process any + * incoming or outgoing data. Do all of this until @ref done returns true (or + * there is an exception). Finally, call @ref produce to get the completed + * connection. + * + * For example: + * + * ```cxx + * pqxx::connecting cg{}; + * + * // Loop until we're done connecting. + * while (!cg.done()) + * { + * wait_for_fd(cg.sock(), cg.wait_to_read(), cg.wait_to_write()); + * cg.process(); + * } + * + * pqxx::connection conn = std::move(cg).produce(); + * + * // At this point, conn is a working connection. You can no longer use + * // cg at all. + * ``` + */ +class PQXX_LIBEXPORT connecting +{ +public: + /// Start connecting. + connecting(zview connection_string = ""_zv); + + connecting(connecting const &) = delete; + connecting(connecting &&) = default; + connecting &operator=(connecting const &) = delete; + connecting &operator=(connecting &&) = default; + + /// Get the socket. The socket may change during the connection process. + [[nodiscard]] int sock() const &noexcept { return m_conn.sock(); } + + /// Should we currently wait to be able to _read_ from the socket? + [[nodiscard]] constexpr bool wait_to_read() const &noexcept + { + return m_reading; + } + + /// Should we currently wait to be able to _write_ to the socket? + [[nodiscard]] constexpr bool wait_to_write() const &noexcept + { + return m_writing; + } + + /// Progress towards completion (but don't block). + void process() &; + + /// Is our connection finished? + [[nodiscard]] constexpr bool done() const &noexcept + { + return not m_reading and not m_writing; + } + + /// Produce the completed connection object. + /** Use this only once, after @ref done returned `true`. Once you have + * called this, the `connecting` instance has no more use or meaning. You + * can't call any of its member functions afterwards. + * + * This member function is rvalue-qualified, meaning that you can only call + * it on an rvalue instance of the class. If what you have is not an rvalue, + * turn it into one by wrapping it in `std::move()`. + */ + [[nodiscard]] connection produce() &&; + +private: + connection m_conn; + bool m_reading{false}; + bool m_writing{true}; +}; + + +template inline std::string connection::quote(T const &t) const +{ + if constexpr (nullness::always_null) + { + return "NULL"; + } + else + { + if (is_null(t)) + return "NULL"; + auto const text{to_string(t)}; + + // Okay, there's an easy way to do this and there's a hard way. The easy + // way was "quote, esc(to_string(t)), quote". I'm going with the hard way + // because it's going to save some string manipulation that will probably + // incur some unnecessary memory allocations and deallocations. + std::string buf{'\''}; + buf.resize(2 + 2 * std::size(text) + 1); + auto const content_bytes{esc_to_buf(text, buf.data() + 1)}; + auto const closing_quote{1 + content_bytes}; + buf[closing_quote] = '\''; + auto const end{closing_quote + 1}; + buf.resize(end); + return buf; + } +} + + +template +inline std::string connection::quote_columns(STRINGS const &columns) const +{ + return separated_list( + ","sv, std::cbegin(columns), std::cend(columns), + [this](auto col) { return this->quote_name(*col); }); +} + + +#if defined(PQXX_HAVE_CONCEPTS) +template +inline connection::connection(MAPPING const ¶ms) +{ + check_version(); + + std::vector keys, values; + if constexpr (std::ranges::sized_range) + { + auto const size{std::ranges::size(params) + 1}; + keys.reserve(size); + values.reserve(size); + } + for (auto const &[key, value] : params) + { + keys.push_back(internal::as_c_string(key)); + values.push_back(internal::as_c_string(value)); + } + keys.push_back(nullptr); + values.push_back(nullptr); + init(std::data(keys), std::data(values)); +} +#endif // PQXX_HAVE_CONCEPTS +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/cursor b/ext/libpqxx-7.7.3/include/pqxx/cursor new file mode 100644 index 000000000..e20b3a4fa --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/cursor @@ -0,0 +1,8 @@ +/** Definition of the iterator/container-style cursor classes. + * + * C++-style wrappers for SQL cursors + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/cursor.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/cursor.hxx b/ext/libpqxx-7.7.3/include/pqxx/cursor.hxx new file mode 100644 index 000000000..b392e2407 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/cursor.hxx @@ -0,0 +1,483 @@ +/* Definition of the iterator/container-style cursor classes. + * + * C++-style wrappers for SQL cursors. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/cursor instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_CURSOR +#define PQXX_H_CURSOR + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + +#include "pqxx/result.hxx" +#include "pqxx/transaction_base.hxx" + + +namespace pqxx +{ +/// Common definitions for cursor types +/** In C++ terms, fetches are always done in pre-increment or pre-decrement + * fashion--i.e. the result does not include the row the cursor is on at the + * beginning of the fetch, and the cursor ends up being positioned on the last + * row in the result. + * + * There are singular positions akin to `end()` at both the beginning and the + * end of the cursor's range of movement, although these fit in so naturally + * with the semantics that one rarely notices them. The cursor begins at the + * first of these, but any fetch in the forward direction will move the cursor + * off this position and onto the first row before returning anything. + */ +class PQXX_LIBEXPORT cursor_base +{ +public: + using size_type = result_size_type; + using difference_type = result_difference_type; + + /// Cursor access-pattern policy + /** Allowing a cursor to move forward only can result in better performance, + * so use this access policy whenever possible. + */ + enum access_policy + { + /// Cursor can move forward only + forward_only, + /// Cursor can move back and forth + random_access + }; + + /// Cursor update policy + /** + * @warning Not all PostgreSQL versions support updatable cursors. + */ + enum update_policy + { + /// Cursor can be used to read data but not to write + read_only, + /// Cursor can be used to update data as well as read it + update + }; + + /// Cursor destruction policy + /** The normal thing to do is to make a cursor object the owner of the SQL + * cursor it represents. There may be cases, however, where a cursor needs + * to persist beyond the end of the current transaction (and thus also beyond + * the lifetime of the cursor object that created it!), where it can be + * "adopted" into a new cursor object. See the basic_cursor documentation + * for an explanation of cursor adoption. + * + * If a cursor is created with "loose" ownership policy, the object + * representing the underlying SQL cursor will not take the latter with it + * when its own lifetime ends, nor will its originating transaction. + * + * @warning Use this feature with care and moderation. Only one cursor + * object should be responsible for any one underlying SQL cursor at any + * given time. + */ + enum ownership_policy + { + /// Destroy SQL cursor when cursor object is closed at end of transaction + owned, + /// Leave SQL cursor in existence after close of object and transaction + loose + }; + + cursor_base() = delete; + cursor_base(cursor_base const &) = delete; + cursor_base &operator=(cursor_base const &) = delete; + + /** + * @name Special movement distances. + */ + //@{ + + // TODO: Make constexpr inline (but breaks ABI). + /// Special value: read until end. + /** @return Maximum value for result::difference_type, so the cursor will + * attempt to read the largest possible result set. + */ + [[nodiscard]] static difference_type all() noexcept; + + /// Special value: read one row only. + /** @return Unsurprisingly, 1. + */ + [[nodiscard]] static constexpr difference_type next() noexcept { return 1; } + + /// Special value: read backwards, one row only. + /** @return Unsurprisingly, -1. + */ + [[nodiscard]] static constexpr difference_type prior() noexcept + { + return -1; + } + + // TODO: Make constexpr inline (but breaks ABI). + /// Special value: read backwards from current position back to origin. + /** @return Minimum value for result::difference_type. + */ + [[nodiscard]] static difference_type backward_all() noexcept; + + //@} + + /// Name of underlying SQL cursor + /** + * @returns Name of SQL cursor, which may differ from original given name. + * @warning Don't use this to access the SQL cursor directly without going + * through the provided wrapper classes! + */ + [[nodiscard]] constexpr std::string const &name() const noexcept + { + return m_name; + } + +protected: + cursor_base(connection &, std::string_view Name, bool embellish_name = true); + + std::string const m_name; +}; +} // namespace pqxx + + +#include + + +namespace pqxx +{ +/// "Stateless cursor" class: easy API for retrieving parts of result sets +/** This is a front-end for SQL cursors, but with a more C++-like API. + * + * Actually, stateless_cursor feels entirely different from SQL cursors. You + * don't keep track of positions, fetches, and moves; you just say which rows + * you want. See the retrieve() member function. + */ +template +class stateless_cursor +{ +public: + using size_type = result_size_type; + using difference_type = result_difference_type; + + /// Create cursor. + /** + * @param tx The transaction within which you want to create the cursor. + * @param query The SQL query whose results the cursor should traverse. + * @param cname A hint for the cursor's name. The actual SQL cursor's name + * will be based on this (though not necessarily identical). + * @param hold Create a `WITH HOLD` cursor? Such cursors stay alive after + * the transaction has ended, so you can continue to use it. + */ + stateless_cursor( + transaction_base &tx, std::string_view query, std::string_view cname, + bool hold) : + m_cur{tx, query, cname, cursor_base::random_access, up, op, hold} + {} + + /// Adopt an existing scrolling SQL cursor. + /** This lets you define a cursor yourself, and then wrap it in a + * libpqxx-managed `stateless_cursor` object. + * + * @param tx The transaction within which you want to manage the cursor. + * @param adopted_cursor Your cursor's SQL name. + */ + stateless_cursor(transaction_base &tx, std::string_view adopted_cursor) : + m_cur{tx, adopted_cursor, op} + { + // Put cursor in known position + m_cur.move(cursor_base::backward_all()); + } + + /// Close this cursor. + /** The destructor will do this for you automatically. + * + * Closing a cursor is idempotent. Closing a cursor that's already closed + * does nothing. + */ + void close() noexcept { m_cur.close(); } + + /// Number of rows in cursor's result set + /** @note This function is not const; it may need to scroll to find the size + * of the result set. + */ + [[nodiscard]] size_type size() + { + return internal::obtain_stateless_cursor_size(m_cur); + } + + /// Retrieve rows from begin_pos (inclusive) to end_pos (exclusive) + /** Rows are numbered starting from 0 to size()-1. + * + * @param begin_pos First row to retrieve. May be one row beyond the end of + * the result set, to avoid errors for empty result sets. Otherwise, must be + * a valid row number in the result set. + * @param end_pos Row up to which to fetch. Rows are returned ordered from + * begin_pos to end_pos, i.e. in ascending order if begin_pos < end_pos but + * in descending order if begin_pos > end_pos. The end_pos may be + * arbitrarily inside or outside the result set; only existing rows are + * included in the result. + */ + result retrieve(difference_type begin_pos, difference_type end_pos) + { + return internal::stateless_cursor_retrieve( + m_cur, result::difference_type(size()), begin_pos, end_pos); + } + + /// Return this cursor's name. + [[nodiscard]] constexpr std::string const &name() const noexcept + { + return m_cur.name(); + } + +private: + internal::sql_cursor m_cur; +}; + + +class icursor_iterator; +} // namespace pqxx + + +namespace pqxx::internal::gate +{ +class icursor_iterator_icursorstream; +class icursorstream_icursor_iterator; +} // namespace pqxx::internal::gate + + +namespace pqxx +{ +/// Simple read-only cursor represented as a stream of results +/** SQL cursors can be tricky, especially in C++ since the two languages seem + * to have been designed on different planets. An SQL cursor has two singular + * positions akin to `end()` on either side of the underlying result set. + * + * These cultural differences are hidden from view somewhat by libpqxx, which + * tries to make SQL cursors behave more like familiar C++ entities such as + * iterators, sequences, streams, and containers. + * + * Data is fetched from the cursor as a sequence of result objects. Each of + * these will contain the number of rows defined as the stream's stride, except + * of course the last block of data which may contain fewer rows. + * + * This class can create or adopt cursors that live outside any backend + * transaction, which your backend version may not support. + */ +class PQXX_LIBEXPORT icursorstream +{ +public: + using size_type = cursor_base::size_type; + using difference_type = cursor_base::difference_type; + + /// Set up a read-only, forward-only cursor. + /** Roughly equivalent to a C++ Standard Library istream, this cursor type + * supports only two operations: reading a block of rows while moving + * forward, and moving forward without reading any data. + * + * @param context Transaction context in which this cursor will be active. + * @param query SQL query whose results this cursor shall iterate. + * @param basename Suggested name for the SQL cursor; the library will append + * a unique code to ensure its uniqueness. + * @param sstride Number of rows to fetch per read operation; must be a + * positive number. + */ + icursorstream( + transaction_base &context, std::string_view query, + std::string_view basename, difference_type sstride = 1); + + /// Adopt existing SQL cursor. Use with care. + /** Forms a cursor stream around an existing SQL cursor, as returned by e.g. + * a server-side function. The SQL cursor will be cleaned up by the stream's + * destructor as if it had been created by the stream; cleaning it up by hand + * or adopting the same cursor twice is an error. + * + * Passing the name of the cursor as a string is not allowed, both to avoid + * confusion with the other constructor and to discourage unnecessary use of + * adopted cursors. + * + * @warning It is technically possible to adopt a "WITH HOLD" cursor, i.e. a + * cursor that stays alive outside its creating transaction. However, any + * cursor stream (including the underlying SQL cursor, naturally) must be + * destroyed before its transaction context object is destroyed. Therefore + * the only way to use SQL's WITH HOLD feature is to adopt the cursor, but + * defer doing so until after entering the transaction context that will + * eventually destroy it. + * + * @param context Transaction context in which this cursor will be active. + * @param cname Result field containing the name of the SQL cursor to adopt. + * @param sstride Number of rows to fetch per read operation; must be a + * positive number. + * @param op Ownership policy. Determines whether the cursor underlying this + * stream will be destroyed when the stream is closed. + */ + icursorstream( + transaction_base &context, field const &cname, difference_type sstride = 1, + cursor_base::ownership_policy op = cursor_base::owned); + + /// Return `true` if this stream may still return more data. + constexpr operator bool() const &noexcept { return not m_done; } + + /// Read new value into given result object; same as operator `>>`. + /** The result set may continue any number of rows from zero to the chosen + * stride, inclusive. An empty result will only be returned if there are no + * more rows to retrieve. + * + * @param res Write the retrieved data into this result object. + * @return Reference to this very stream, to facilitate "chained" invocations + * ("C.get(r1).get(r2);") + */ + icursorstream &get(result &res) + { + res = fetchblock(); + return *this; + } + /// Read new value into given result object; same as `get(result&)`. + /** The result set may continue any number of rows from zero to the chosen + * stride, inclusive. An empty result will only be returned if there are no + * more rows to retrieve. + * + * @param res Write the retrieved data into this result object. + * @return Reference to this very stream, to facilitate "chained" invocations + * ("C >> r1 >> r2;") + */ + icursorstream &operator>>(result &res) { return get(res); } + + /// Move given number of rows forward without reading data. + /** Ignores any stride that you may have set. It moves by a given number of + * rows, not a number of strides. + * + * @return Reference to this stream itself, to facilitate "chained" + * invocations. + */ + icursorstream &ignore(std::streamsize n = 1) &; + + /// Change stride, i.e. the number of rows to fetch per read operation. + /** + * @param stride Must be a positive number. + */ + void set_stride(difference_type stride) &; + [[nodiscard]] constexpr difference_type stride() const noexcept + { + return m_stride; + } + +private: + result fetchblock(); + + friend class internal::gate::icursorstream_icursor_iterator; + size_type forward(size_type n = 1); + void insert_iterator(icursor_iterator *) noexcept; + void remove_iterator(icursor_iterator *) const noexcept; + + void service_iterators(difference_type); + + internal::sql_cursor m_cur; + + difference_type m_stride; + difference_type m_realpos, m_reqpos; + + mutable icursor_iterator *m_iterators; + + bool m_done; +}; + + +/// Approximate istream_iterator for icursorstream. +/** Intended as an implementation of an input_iterator (as defined by the C++ + * Standard Library), this class supports only two basic operations: reading + * the current element, and moving forward. In addition to the minimal + * guarantees for istream_iterators, this class supports multiple successive + * reads of the same position (the current result set is cached in the + * iterator) even after copying and even after new data have been read from the + * stream. This appears to be a requirement for input_iterators. Comparisons + * are also supported in the general case. + * + * The iterator does not care about its own position, however. Moving an + * iterator forward moves the underlying stream forward and reads the data from + * the new stream position, regardless of the iterator's old position in the + * stream. + * + * The stream's stride defines the granularity for all iterator movement or + * access operations, i.e. "ici += 1" advances the stream by one stride's worth + * of rows, and "*ici++" reads one stride's worth of rows from the stream. + * + * @warning Do not read from the underlying stream or its cursor, move its read + * position, or change its stride, between the time the first icursor_iterator + * on it is created and the time its last icursor_iterator is destroyed. + * + * @warning Manipulating these iterators within the context of a single cursor + * stream is not thread-safe. Creating a new iterator, copying one, + * or destroying one affects the stream as a whole. + */ +class PQXX_LIBEXPORT icursor_iterator +{ +public: + using iterator_category = std::input_iterator_tag; + using value_type = result; + using pointer = result const *; + using reference = result const &; + using istream_type = icursorstream; + using size_type = istream_type::size_type; + using difference_type = istream_type::difference_type; + + icursor_iterator() noexcept; + explicit icursor_iterator(istream_type &) noexcept; + icursor_iterator(icursor_iterator const &) noexcept; + ~icursor_iterator() noexcept; + + result const &operator*() const + { + refresh(); + return m_here; + } + result const *operator->() const + { + refresh(); + return &m_here; + } + icursor_iterator &operator++(); + icursor_iterator operator++(int); + icursor_iterator &operator+=(difference_type); + icursor_iterator &operator=(icursor_iterator const &) noexcept; + + [[nodiscard]] bool operator==(icursor_iterator const &rhs) const; + [[nodiscard]] bool operator!=(icursor_iterator const &rhs) const noexcept + { + return not operator==(rhs); + } + [[nodiscard]] bool operator<(icursor_iterator const &rhs) const; + [[nodiscard]] bool operator>(icursor_iterator const &rhs) const + { + return rhs < *this; + } + [[nodiscard]] bool operator<=(icursor_iterator const &rhs) const + { + return not(*this > rhs); + } + [[nodiscard]] bool operator>=(icursor_iterator const &rhs) const + { + return not(*this < rhs); + } + +private: + void refresh() const; + + friend class internal::gate::icursor_iterator_icursorstream; + difference_type pos() const noexcept { return m_pos; } + void fill(result const &); + + icursorstream *m_stream{nullptr}; + result m_here; + difference_type m_pos; + icursor_iterator *m_prev{nullptr}, *m_next{nullptr}; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/dbtransaction b/ext/libpqxx-7.7.3/include/pqxx/dbtransaction new file mode 100644 index 000000000..fa8d26476 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/dbtransaction @@ -0,0 +1,8 @@ +/** pqxx::dbtransaction abstract base class. + * + * pqxx::dbransaction defines a real transaction on the database. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/dbtransaction.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/dbtransaction.hxx b/ext/libpqxx-7.7.3/include/pqxx/dbtransaction.hxx new file mode 100644 index 000000000..d85cb170f --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/dbtransaction.hxx @@ -0,0 +1,70 @@ +/* Definition of the pqxx::dbtransaction abstract base class. + * + * pqxx::dbransaction defines a real transaction on the database. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/dbtransaction instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_DBTRANSACTION +#define PQXX_H_DBTRANSACTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/transaction_base.hxx" + +namespace pqxx +{ +/// Abstract transaction base class: bracket transactions on the database. +/** + * @ingroup transactions + * + * Use a dbtransaction-derived object such as "work" (transaction<>) to enclose + * operations on a database in a single "unit of work." This ensures that the + * whole series of operations either succeeds as a whole or fails completely. + * In no case will it leave half-finished work behind in the database. + * + * Once processing on a transaction has succeeded and any changes should be + * allowed to become permanent in the database, call commit(). If something + * has gone wrong and the changes should be forgotten, call abort() instead. + * If you do neither, an implicit abort() is executed at destruction time. + * + * It is an error to abort a transaction that has already been committed, or to + * commit a transaction that has already been aborted. Aborting an already + * aborted transaction or committing an already committed one is allowed, to + * make error handling easier. Repeated aborts or commits have no effect after + * the first one. + * + * Database transactions are not suitable for guarding long-running processes. + * If your transaction code becomes too long or too complex, consider ways to + * break it up into smaller ones. Unfortunately there is no universal recipe + * for this. + * + * The actual operations for committing/aborting the backend transaction are + * implemented by a derived class. The implementing concrete class must also + * call @ref close from its destructor. + */ +class PQXX_LIBEXPORT PQXX_NOVTABLE dbtransaction : public transaction_base +{ +protected: + /// Begin transaction. + explicit dbtransaction(connection &c) : transaction_base{c} {} + /// Begin transaction. + dbtransaction(connection &c, std::string_view tname) : + transaction_base{c, tname} + {} + /// Begin transaction. + dbtransaction( + connection &c, std::string_view tname, + std::shared_ptr rollback_cmd) : + transaction_base{c, tname, rollback_cmd} + {} +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/accessing-results.md b/ext/libpqxx-7.7.3/include/pqxx/doc/accessing-results.md new file mode 100644 index 000000000..920fb6f3b --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/accessing-results.md @@ -0,0 +1,157 @@ +Accessing results and result rows {#accessing-results} +--------------------------------- + +When you execute a query using one of the transaction `exec` functions, you +normally get a `result` object back. A `result` is a container of `row`s. + +(There are exceptions. The `exec1` functions expect exactly one row of data, +so they return just a `row`, not a full `result`.) + +Result objects are an all-or-nothing affair. The `exec` function waits until +it's received all the result data, and then gives it to you in the form of the +`result`. _(There is a faster, easier way of executing simple queries, so see +"streaming rows" below as well.)_ + +For example, your code might do: + +```cxx + pqxx::result r = tx.exec("SELECT * FROM mytable"); +``` + +Now, how do you access the data inside `r`? + +Result sets act as standard C++ containers of rows. Rows act as standard +C++ containers of fields. So the easiest way to go through them is: + +```cxx + for (auto const &row: r) + { + for (auto const &field: row) std::cout << field.c_str() << '\t'; + std::cout << '\n'; + } +``` + +But results and rows also support other kinds of access. Array-style +indexing, for instance, such as `r[rownum]`: + +```cxx + std::size_t const num_rows = std::size(r); + for (std::size_t rownum=0u; rownum < num_rows; ++rownum) + { + pqxx::row const row = r[rownum]; + std::size_t const num_cols = std::size(row); + for (std::size_t colnum=0u; colnum < num_cols; ++colnum) + { + pqxx::field const field = row[colnum]; + std::cout << field.c_str() << '\t'; + } + + std::cout << '\n'; + } +``` + +Every row in the result has the same number of columns, so you don't need to +look up the number of fields again for each one: + +```cxx + std::size_t const num_rows = std::size(r); + std::size_t const num_cols = r.columns(); + for (std::size_t rownum=0u; rownum < num_rows; ++rownum) + { + pqxx::row const row = r[rownum]; + for (std::size_t colnum=0u; colnum < num_cols; ++colnum) + { + pqxx::field const field = row[colnum]; + std::cout << field.c_str() << '\t'; + } + + std::cout << '\n'; + } +``` + +You can even address a field by indexing the `row` using the field's _name:_ + +```cxx + std::cout << row["salary"] << '\n'; +``` + +But try not to do that if speed matters, because looking up the column by name +takes time. At least you'd want to look up the column index before your loop +and then use numerical indexes inside the loop. + +For C++23 or better, there's also a two-dimensional array access operator: + +```cxx + for (std::size_t rownum=0u; rownum < num_rows; ++rownum) + { + for (std::size_t colnum=0u; colnum < num_cols; ++colnum) + std::cout result[rownum, colnum].c_str() << '\t'; + std::cout << '\n'; + } +``` + +And of course you can use classic "begin/end" loops: + +```cxx + for (auto row = std::begin(r); row != std::end(r); row++) + { + for (auto field = std::begin(row); field != std::end(row); field++) + std::cout << field->c_str() << '\t'; + std::cout << '\n'; + } +``` + +Result sets are immutable, so all iterators on results and rows are actually +`const_iterator`s. There are also `const_reverse_iterator` types, which +iterate backwards from `rbegin()` to `rend()` exclusive. + +All these iterator types provide one extra bit of convenience that you won't +normally find in C++ iterators: referential transparency. You don't need to +dereference them to get to the row or field they refer to. That is, instead +of `row->end()` you can also choose to say `row.end()`. Similarly, you +may prefer `field.c_str()` over `field->c_str()`. + +This becomes really helpful with the array-indexing operator. With regular +C++ iterators you would need ugly expressions like `(*row)[0]` or +`row->operator[](0)`. With the iterator types defined by the result and +row classes you can simply say `row[0]`. + + +Streaming rows +-------------- + +There's another way to go through the rows coming out of a query. It's +usually easier and faster, but there are drawbacks. + +**One,** you start getting rows before all the data has come in from the +database. That speeds things up, but what happens if you lose your network +connection while transferring the data? Your application may already have +processed some of the data before finding out that the rest isn't coming. If +that is a problem for your application, streaming may not be the right choice. + +**Two,** streaming only works for some types of query. The `stream()` function +wraps your query in a PostgreSQL `COPY` command, and `COPY` only supports a few +commands: `SELECT`, `VALUES`, `or an `INSERT`, `UPDATE`, or `DELETE` with a +`RETURNING` clause. See the `COPY` documentation here: +https://www.postgresql.org/docs/current/sql-copy.html + +**Three,** when you convert a field to a "view" type (such as +`std::string_view` or `std::basic_string_view`), the view points to +underlying data which only stays valid until you iterate to the next row or +exit the loop. So if you want to use that data for longer than a single +iteration of the streaming loop, you'll have to store it somewhere yourself. + +Now for the good news. Streaming does make it very easy to query data and loop +over it: + +```cxx + for (auto [id, name, x, y] : + tx.stream( + "SELECT id, name, x, y FROM point")) + process(id + 1, "point-" + name, x * 10.0, y * 10.0); +``` + +The conversion to C++ types (here `int`, `std::string_view`, and two `float`s) +is built into the function. You never even see `row` objects, `field` objects, +iterators, or conversion methods. You just put in your query and you receive +your data. diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/binary-data.md b/ext/libpqxx-7.7.3/include/pqxx/doc/binary-data.md new file mode 100644 index 000000000..20da8dc0c --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/binary-data.md @@ -0,0 +1,56 @@ +Binary data {#binary} +=========== + +The database has two ways of storing binary data: `BYTEA` is like a string, but +containing bytes rather than text characters. And _large objects_ are more +like a separate table containing binary objects. + +Generally you'll want to use `BYTEA` for reasonably-sized values, and large +objects for very large values. + +That's the database side. On the C++ side, in libpqxx, all binary data must be +either `std::basic_string` or `std::basic_string_view`; +or if you're building in C++20 or better, anything that's a block of +contiguous `std::byte` in memory. + +So for example, if you want to write a large object, you'd create a +`pqxx::blob` object. And you might use that to write data in the form of +`std::basic_string_view`. + +Your particular binary data may look different though. You may have it in a +`std::string`, or a `std::vector`, or a pointer to `char` +accompanied by a size (which could be signed or unsigned, and of any of a few +different widths). Sometimes that's your choice, or sometimes some other +library will dictate what form it takes. + +So long as it's _basically_ still a block of bytes though, you can use +`pqxx::binary_cast` to construct a `std::basic_string_view` from it. + +There are two forms of `binary_cast`. One takes a single argument that must +support `std::data()` and `std::size()`: + + std::string hi{"Hello binary world"}; + my_blob.write(pqxx::binary_cast(hi); + +The other takes a pointer and a size: + + char const greeting[] = "Hello binary world"; + char const *hi = greeting; + my_blob.write(pqxx::binary_cast(hi, sizeof(greeting))); + + +Caveats +------- + +There are some restrictions on `binary_cast` that you must be aware of. + +First, your data must of a type that gives us _bytes._ So: `char`, +`unsigned char`, `signed char`, `int8_t`, `uint8_t`, or of course `std::byte`. +You can't feed in a vector of `double`, or anything like that. + +Second, the data must be laid out as a contiguous block in memory. If there's +no `std::data()` implementation for your type, it's not suitable. + +Third, `binary_cast` only constructs something like a `std::string_view`. It +does not make a copy of your actual data. So, make sure that your data remains +alive and in the same place while you're using it. diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/datatypes.md b/ext/libpqxx-7.7.3/include/pqxx/doc/datatypes.md new file mode 100644 index 000000000..bc14c8b90 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/datatypes.md @@ -0,0 +1,373 @@ +Supporting additional data types {#datatypes} +================================ + +Communication with the database mostly happens in a text format. When you +include an integer value in a query, you use `to_string` to convert it to that +text format. When you get a query result field "as a float," it converts from +the text format to a floating-point type. These conversions are everywhere in +libpqxx. + +The conversion sydstem supports many built-in types, but it is also extensible. +You can "teach" libpqxx (in the scope of your own application) to convert +additional types of values to and from PostgreSQL's string format. + +This is massively useful, but it's not for the faint of heart. You'll need to +specialise some templates. And, **the API for doing this can change with any +major libpqxx release.** + + +Converting types +---------------- + +In your application, a conversion is driven entirely by a C++ type you specify. +The value's SQL type has nothing to do with it, nor is there anything in the +string that would identify its type. + +So, if you've SELECTed a 64-bit integer from the database, and you try to +convert it to a C++ "short," one of two things will happen: either the number +is small enough to fit in your `short` (and it just works), or else it throws a +conversion exception. + +Or, your database table might have a text column, but a given field may contain +a string that _looks_ just like a number. You can convert that value to an +integer type just fine. Or to a floating-point type. All that matters to the +conversion is the actual value, and the type. + +In some cases the templates for these conversions can tell the type from the +arguments you pass them: + + auto x = to_string(99); + +In other cases you may need to instantiate template explicitly: + + auto y = from_string("99"); + + +Supporting a new type +--------------------- + +Let's say you have some other SQL type which you want to be able to store in, +or retrieve from, the database. What would it take to support that? + +Sometimes you do not need _complete_ support. You might need a conversion _to_ +a string but not _from_ a string, for example. The conversion is defined at +compile time, so don't be too afraid to be incomplete. If you leave out one of +these steps, it's not going to crash at run time or mess up your data. The +worst that can happen is that your code won't build. + +So what do you need for a complete conversion? + +First off, of course, you need a C++ type. It may be your own, but it +doesn't have to be. It could be a type from a third-party library, or even one +from the standard library that libpqxx does not yet support. + +You also specialise the `pqxx::type_name` variable to specify the type's name. +This is important for all code which mentions your type in human-readable text, +such as error messages. + +Then, does your type have a built-in null value? You specialise the +`pqxx::nullness` template to specify the details. + +Finally, you specialise the `pqxx::string_traits` template. This is where you +define the actual conversions. + +Let's go through these steps one by one. + + +Your type +--------- + +You'll need a type for which the conversions are not yet defined, because the +C++ type is what determines the right conversion. One type, one set of +conversions. + +The type doesn't have to be one that you create. The conversion logic was +designed such that you can build it around any type. So you can just as +easily build a conversion for a type that's defined somewhere else. There's +no need to include any special methods or other members inside it. That's also +how libpqxx can support converting built-in types like `int`. + +By the way, if the type is an enum, you don't need to do any of this. Just +invoke the preprocessor macro `PQXX_DECLARE_ENUM_CONVERSION`, from the global +namespace near the top of your translation unit, and pass the type as an +argument. + +The library also provides specialisations for `std::optional`, +`std::shared_ptr`, and `std::unique_ptr`. If you have conversions for +`T`, you'll also have conversions for those. + + +Specialise `type_name` +---------------------- + +When errors happen during conversion, libpqxx will compose error messages for +the user. Sometimes these will include the name of the type that's being +converted. + +To tell libpqxx the name of each type, there's a template variable called +`pqxx::type_name`. For any given type `T`, it should have a specialisation +that provides that `T`'s human-readable name: + + namespace pqxx + { + template<> std::string const type_name{"T"}; + } + +(Yes, this means that you need to define something inside the pqxx namespace. +Future versions of libpqxx may move this into a separate namespace.) + +Define this early on in your translation unit, before any code that might cause +libpqxx to need the name. That way, the libpqxx code which needs to know the +type's name can see your definition. + + +Specialise `nullness` +--------------------- + +A struct template `pqxx::nullness` defines whether your type has a natural +"null value" built in. If so, it also provides member functions for producing +and recognising null values. + +The simplest scenario is also the most common: most types don't have a null +value built in. In that case, derive your nullness traits from +`pqxx::no_null`: + + namespace pqxx + { + template<> struct nullness : pqxx::no_null {}; + } + +(Here again you're defining this in the pqxx namespace.) + +If your type does have a natural null value, the definition gets a little more +complex: + + namespace pqxx + { + template<> struct nullness + { + static constexpr bool has_null{true}; + static constexpr bool always_null{false}; + + static bool is_null(T const &value) + { + // Return whether "value" is null. + return ...; + } + + [[nodiscard]] static T null() + { + // Return a null value. + return ...; + } + }; + } + +You may be wondering why there's a function to produce a null value, but also a +function to check whether a value is null. Why not just compare the value to +the result of `null()`? Because two null values may not be equal. `T` may +have several different null values. Or it may override the comparison +operator, similar to SQL where NULL is not equal to NULL. + +As a third case, your type may be one that _always_ represents a null value. +This is the case for `std::nullptr_t` and `std::nullopt_t`. In that case, you +set `nullness::always_null` to `true` (as well as `has_null` of course), +and you won't need to define any actual conversions. + + +Specialise `string_traits` +------------------------- + +This part is more work. (You can skip it for types that are _always_ null, +but those will be rare.) Specialise the `pqxx::string_traits` template: + + namespace pqxx + { + template<> struct string_traits + { + static T from_string(std::string_view text); + static zview to_buf(char *begin, char *end, T const &value); + static char *into_buf(char *begin, char *end, T const &value); + static std::size_t size_buffer(T const &value) noexcept; + }; + } + +You'll also need to write those member functions, or as many of them as needed +to get your code to build. + + +### `from_string` + +We start off simple: `from_string` parses a string as a value of `T`, and +returns that value. + +The string may not be zero-terminated; it's just the `string_view` from +beginning to end (exclusive). In your tests, cover cases where the string +does not end in a zero byte. + +It's perfectly possible that the string isn't actually a `T` value. Mistakes +happen. In that case, throw a `pqxx::conversion_error`. + +(Of course it's also possible that you run into some other error, so it's fine +to throw different exceptions as well. But when it's definitely "this is not +the right format for a `T`," throw `conversion_error`.) + + +### `to_buf` + +In this function, you convert a value of `T` into a string that the postgres +server will understand. + +The caller will provide you with a buffer where you can write the string, if +you need it: from `begin` to `end` exclusive. It's a half-open interval, so +don't access `*end`. + +If the buffer is insufficient for you to do the conversion, throw a +`pqxx::conversion_overrun`. It doesn't have to be exact: you can be a little +pessimistic and demand a bit more space than you need. Just be sure to throw +the exception if there's any risk of overrunning the buffer. + +You don't _have_ to use the buffer for this function though. For example, +`pqxx::string_traits::to_buf` returns a compile-time constant string and +ignores the buffer. + +Even if you do use the buffer, your string does not _have_ to start at the +beginning of the buffer. For example, the integer conversions start by writing +the _least_ significant digit to the _end_ of the buffer, and then writes the +more significant digits before it. It was just more convenient. + +Return a `pqxx::zview`. This is basically a `std::string_view`, but with one +difference: a `zview` guarantees that there will be a valid zero byte right +after the `string_view`. The zero byte is not counted as part of its size, but +it will be there. + +Expressed in code, this rule must hold: + + void invariant(zview z) + { + assert(z[std::size(z)] == 0); + } + +Make sure you write your trailing zero _before_ the `end`. If the trailing +zero doesn't fit in the buffer, then there's just not enough room to perform +the conversion. + +Beware of locales when converting. If you use standard library features like +`sprintf`, they may obey whatever locale is currently set on the system. That +means that a simple integer like 1000000 may come out as "1000000" on your +system, but as "1,000,000" on mine, or as "1.000.000" for somebody else, and on +an Indian system it may be "1,00,000". Values coming from or going to the +database should be in non-localised formats. You can use libpqxx functions for +those conversions: `pqxx::from_string`, `pqxx::to_string`, `pqxx::to_buf`. + + +### `into_buf` + +This is a stricter version of `to_buf`. All the same requirements apply, but +in addition you must write your string into the buffer provided, starting +_exactly_ at `begin`. + +That's why this function returns just a simple pointer: the address right +behind the trailing zero. If the caller wants to use the string, they can +find it at `begin`. If they want to write a different value into the rest of +the buffer, they can start at the location you returned. + + +### `size_buffer` + +Here you estimate how much buffer space you need for converting a `T` to a +string. Be precise if you can, but pessimistic if you must. It's usually +better to waste a few unnecessary bytes than to spend a lot of time computing +the exact buffer space you need. And failing the conversion because you +under-budgeted the buffer is worst of all. + +Include the trailing zero in the buffer size. If your `to_buf` takes more +space than just what's needed to store the result, include that too. + +Make `size_buffer` a `constexpr` function if you can. It can allow the caller +to allocate the buffer on the stack, with a size known at compile time. + + +Optional: Specialise `is_unquoted_safe` +--------------------------------------- + +When converting arrays or composite values to strings, libpqxx may need to +quote values and escape any special characters. This takes time. + +Some types though, such as integral or floating-point types, can never have +any special characters such as quotes, commas, or backslashes in their string +representations. In such cases, there's no need to quote or escape such values +in arrays or composite types. + +If your type is like that, you can tell libpqxx about this by defining: + + namespace pqxx + { + template<> inline constexpr bool is_unquoted_safe{true}; + } + +The code that converts this type of field to strings in an array or a composite +type can then use a simpler, more efficient variant of the code. It's always +safe to leave this out; it's _just_ an optimisation for when you're completely +sure that it's safe. + +Do not do this if a string representation of your type may contain a comma; +semicolon; parenthesis; brace; quote; backslash; newline; or any other +character that might need escaping. + + +Optional: Specialise `param_format` +----------------------------------- + +This one you don't generally need to worry about. Read on if you're writing a +type which represents raw binary data, or if you're writing a template where +_some specialisations_ may contain raw binary data. + +When you call parameterised statements, or prepared statements with parameters, +libpqxx needs to your parameters on to libpq, the underlying C-level PostgreSQL +client library. + +There are two formats for doing that: _text_ and _binary._ In the first, we +represent all values as strings, and the server then converts them into its own +internal binary representation. That's what the string conversions are all +about, and it's what we do for almost all types of parameters. + +But we do it differently when the parameter is a contiguous series of raw bytes +and the corresponding SQL type is `BYTEA`. There is a text format for those, +but we bypass it for efficiency. The server can use the binary data in the +exact same form, without any conversion or extra processing. The binary data +is also twice as compact during transport. + +(People sometimes ask why we can't just treat all types as binary. However the +general case isn't so clear-cut. The binary formats are not documented, there +are no guarantees that they will be platform-independent or that they will +remain stable, and there's no really solid way to detect when we might get the +format wrong. But also, the conversions aren't necessarily as straightforward +and efficient as they sound. So, for the general case, libpqxx sticks with the +text formats. Raw binary data alone stands out as a clear win.) + +Long story short, the machinery for passing parameters needs to know: is this +parameter a binary string, or not? In the normal case it can assume "no," and +that's what it does. The text format is always a safe choice; we just try to +use the binary format where it's faster. + +The `param_format` function template is what makes the decision. We specialise +it for types which may be binary strings, and use the default for all other +types. + +"Types which _may_ be binary"? You might think we know whether a type is a +binary type or not. But there are some complications with generic types. + +Templates like `std::shared_ptr`, `std::optional`, and so on act like +"wrappers" for another type. A `std::optional` is binary if `T` is binary. +Otherwise, it's not. If you're building support for a template of this nature, +you'll probably want to implement `param_format` for it. + +The decision to use binary format is made based on a given object, not +necessarily based on the type in general. Look at `std::variant`. If you have +a `std::variant` type which can hold an `int` or a binary string, is that a +binary parameter? We can't decide without knowing the individual object. + +Containers are another hard case. Should we pass `std::vector` in binary? +Even when `T` is a binary type, we don't currently have any way to pass an +array in binary format, so we always pass it as text. diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/escaping.md b/ext/libpqxx-7.7.3/include/pqxx/doc/escaping.md new file mode 100644 index 000000000..2ad9fe3db --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/escaping.md @@ -0,0 +1,74 @@ +String escaping {#escaping} +=============== + +Writing queries as strings is easy. But sometimes you need a variable in +there: `"SELECT id FROM user WHERE name = '" + name + "'"`. + +This is dangerous. See the bug? If `name` can contain quotes, you may have +an SQL injection vulnerability there, where users can enter nasty stuff like +"`.'; DROP TABLE user`". Or if you're lucky, it's just a nasty bug that you +discover when `name` happens to be "d'Arcy". + +So, you'll need to _escape_ the `name` before you insert it. This is where +quotes and other problematic characters are marked as "this is just a character +in the string, not the end of the string." There are +[several functions](@ref escaping-functions) in libpqxx to do this for you. + + +SQL injection +------------- + +To understand what SQL injection vulnerabilities are and why they should be +prevented, imagine you use the following SQL statement somewhere in your +program: + + TX.exec( + "SELECT number,amount " + "FROM accounts " + "WHERE allowed_to_see('" + userid + "','" + password + "')"); + +This shows a logged-in user important information on all accounts he is +authorized to view. The userid and password strings are variables entered +by the user himself. + +Now, if the user is actually an attacker who knows (or can guess) the +general shape of this SQL statement, imagine he enters the following +password: + + x') OR ('x' = 'x + +Does that make sense to you? Probably not. But if this is inserted into +the SQL string by the C++ code above, the query becomes: + + SELECT number,amount + FROM accounts + WHERE allowed_to_see('user','x') OR ('x' = 'x') + +Is this what you wanted to happen? Probably not! The neat `allowed_to_see()` +clause is completely circumvented by the "`OR ('x' = 'x')`" clause, which is +always `true`. Therefore, the attacker will get to see all accounts in the +database! + + +Using the esc functions +----------------------- + +Here's how you can fix the problem in the example above: + + TX.exec( + "SELECT number,amount " + "FROM accounts " + "WHERE allowed_to_see('" + TX.esc(userid) + "', " + "'" + TX.esc(password) + "')"); + +Now, the quotes embedded in the attacker's string will be neatly escaped so +they can't "break out" of the quoted SQL string they were meant to go into: + + SELECT number,amount + FROM accounts + WHERE allowed_to_see('user', 'x'') OR (''x'' = ''x') + +If you look carefully, you'll see that thanks to the added escape characters +(a single-quote is escaped in SQL by doubling it) all we get is a very +strange-looking password string--but not a change in the SQL statement. + diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/getting-started.md b/ext/libpqxx-7.7.3/include/pqxx/doc/getting-started.md new file mode 100644 index 000000000..1b87b881f --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/getting-started.md @@ -0,0 +1,142 @@ +Getting started {#getting-started} +=============== + +The most basic three types in libpqxx are the _connection_, the _transaction_, +and the _result_. + +They fit together as follows: +* You connect to the database by creating a `pqxx::connection` object (see + @ref connections). + +* You create a transaction object (see @ref transactions) operating on that + connection. You'll usually want the `pqxx::work` variety. + + Once you're done you call the transaction's `commit` function to make its + work final. If you don't call this, the work will be rolled back when the + transaction object is destroyed. + +* Until then, use the transaction's `exec`, `query_value`, and `stream` + functions (and variants) to execute SQL statements. You pass the statements + themselves in as simple strings. (See @ref streams for more about data + streaming). + +* Most of the `exec` functions return a `pqxx::result` object, which acts + as a standard container of rows: `pqxx::row`. + + Each row in a result, in turn, acts as a container of fields: `pqxx::field`. + See @ref accessing-results for more about results, rows, and fields. + +* Each field's data is stored internally as a text string, in a format defined + by PostgreSQL. You can convert field or row values using their `as()` and + `to()` member functions. + +* After you've closed the transaction, the connection is free to run a next + transaction. + +Here's a very basic example. It connects to the default database (you'll +need to have one set up), queries it for a very simple result, converts it to +an `int`, and prints it out. It also contains some basic error handling. + + #include + #include + + int main() + { + try + { + // Connect to the database. In practice we may have to pass some + // arguments to say where the database server is, and so on. + // The constructor parses options exactly like libpq's + // PQconnectdb/PQconnect, see: + // https://www.postgresql.org/docs/10/static/libpq-connect.html + pqxx::connection c; + + // Start a transaction. In libpqxx, you always work in one. + pqxx::work w(c); + + // work::exec1() executes a query returning a single row of data. + // We'll just ask the database to return the number 1 to us. + pqxx::row r = w.exec1("SELECT 1"); + + // Commit your transaction. If an exception occurred before this + // point, execution will have left the block, and the transaction will + // have been destroyed along the way. In that case, the failed + // transaction would implicitly abort instead of getting to this point. + w.commit(); + + // Look at the first and only field in the row, parse it as an integer, + // and print it. + // + // "r[0]" returns the first field, which has an "as<...>()" member + // function template to convert its contents from their string format + // to a type of your choice. + std::cout << r[0].as() << std::endl; + } + catch (std::exception const &e) + { + std::cerr << e.what() << std::endl; + return 1; + } + } + +This prints the number 1. Notice that you can keep the result object around +after you've closed the transaction or even the connection. There are +situations where you can't do it, but generally it's fine. If you're +interested: you can install your own callbacks for receiving error messages +from the database, and in that case you'll have to keep the connection object +alive. But otherwise, it's nice to be able to "fire and forget" your +connection and deal with the data. + +You can also convert an entire row to a series of C++-side types in one go, +using the @c as member function on the row: + + pqxx::connection c; + pqxx::work w(c); + pqxx::row r = w.exec1("SELECT 1, 2, 'Hello'"); + auto [one, two, hello] = r.as(); + std::cout << (one + two) << ' ' << std::strlen(hello) << std::endl; + +Here's a slightly more complicated example. It takes an argument from the +command line and retrieves a string with that value. The interesting part is +that it uses the escaping-and-quoting function `quote` to embed this +string value in SQL safely. It also reads the result field's value as a +plain C-style string using its `c_str` function. + + #include + #include + #include + + int main(int argc, char *argv[]) + { + try + { + if (!argv[1]) throw std::runtime_error("Give me a string!"); + + pqxx::connection c; + pqxx::work w(c); + + // work::exec() returns a full result set, which can consist of any + // number of rows. + pqxx::result r = w.exec("SELECT " + w.quote(argv[1])); + + // End our transaction here. We can still use the result afterwards. + w.commit(); + + // Print the first field of the first row. Read it as a C string, + // just like std::string::c_str() does. + std::cout << r[0][0].c_str() << std::endl; + } + catch (std::exception const &e) + { + std::cerr << e.what() << std::endl; + return 1; + } + } + +You can find more about converting field values to native types, or +converting values to strings for use with libpqxx, under +@ref stringconversion. More about getting to the rows and fields of a +result is under @ref accessing-results. + +If you want to handle exceptions thrown by libpqxx in more detail, for +example to print the SQL contents of a query that failed, see @ref exception. diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/mainpage.md b/ext/libpqxx-7.7.3/include/pqxx/doc/mainpage.md new file mode 100644 index 000000000..5d4b8f9b2 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/mainpage.md @@ -0,0 +1,28 @@ +libpqxx {#mainpage} +======= + +@version 7.7.3 +@author Jeroen T. Vermeulen +@see http://pqxx.org +@see https://github.com/jtv/libpqxx + +Welcome to libpqxx, the C++ API to the PostgreSQL database management system. + +Compiling this package requires PostgreSQL to be installed -- including the +C headers for client development. The library builds on top of PostgreSQL's +standard C API, libpq. The libpq headers are not needed to compile client +programs, however. + +For a quick introduction to installing and using libpqxx, see the README.md +file. The latest information can be found at http://pqxx.org/ + + +Some links that should help you find your bearings: +* @ref getting-started +* @ref thread-safety +* @ref connections +* @ref transactions +* @ref escaping +* @ref performance +* @ref transactor +* @ref datatypes diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/mainpage.md.template b/ext/libpqxx-7.7.3/include/pqxx/doc/mainpage.md.template new file mode 100644 index 000000000..d5afb2427 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/mainpage.md.template @@ -0,0 +1,28 @@ +libpqxx {#mainpage} +======= + +@version @PQXXVERSION@ +@author Jeroen T. Vermeulen +@see http://pqxx.org +@see https://github.com/jtv/libpqxx + +Welcome to libpqxx, the C++ API to the PostgreSQL database management system. + +Compiling this package requires PostgreSQL to be installed -- including the +C headers for client development. The library builds on top of PostgreSQL's +standard C API, libpq. The libpq headers are not needed to compile client +programs, however. + +For a quick introduction to installing and using libpqxx, see the README.md +file. The latest information can be found at http://pqxx.org/ + + +Some links that should help you find your bearings: +* @ref getting-started +* @ref thread-safety +* @ref connections +* @ref transactions +* @ref escaping +* @ref performance +* @ref transactor +* @ref datatypes diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/parameters.md b/ext/libpqxx-7.7.3/include/pqxx/doc/parameters.md new file mode 100644 index 000000000..7ac792025 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/parameters.md @@ -0,0 +1,90 @@ +Statement parameters {#parameters} +==================== + +When you execute a prepared statement (see @ref prepared), or a parameterised +statement (using functions like `pqxx::connection::exec_params`), you may write +special _placeholders_ in the query text. They look like `$1`, `$2`, and so +on. + +If you execute the query and pass parameter values, the call will respectively +substitute the first where it finds `$1`, the second where it finds `$2`, et +cetera. + +Doing this saves you work. If you don't use statement parameters, you'll need +to quote and escape your values (see `connection::quote()` and friends) as you +insert them into your query as literal values. + +Or if you forget to do that, you leave yourself open to horrible +[SQL injection attacks](https://xkcd.com/327/). Trust me, I was born in a town +whose name started with an apostrophe! + +Statement parameters save you this work. With these parameters you can pass +your values as-is, and they will go across the wire to the database in a safe +format. + +In some cases it may even be faster! When a parameter represents binary data +(as in the SQL `BYTEA` type), libpqxx will send it directly as binary, which is +a bit more efficient. If you insert the binary data directly in your query +text, your CPU will have some extra work to do, converting the data into a text +format, escaping it, and adding quotes. + + +Dynamic parameter lists +----------------------- + +In rare cases you may just not know how many parameters you'll pass into your +statement when you call it. + +For these situations, have a look at `params`. It lets you compose your +parameters list on the fly, even add whole ranges of parameters at a time. + +You can pass a `params` into your statement as a normal parameter. It will +fill in all the parameter values it contains into that position of the +statement's overall parameter list. + +So if you call your statement passing a regular parameter `a`, a +`params` containing just a parameter `b`, and another regular parameter `c`, +then your call will pass parameters `a`, `b`, and `c`. Or if the params object +is empty, it will pass just `a` and `c`. If the params object contains `x` and +`y`, your call will pass `a, x, y, c`. + +You can mix static and dynamic parameters freely. Don't go overboard though: +complexity is where bugs happen! + + +Generating placeholders +----------------------- + +If your code gets particularly complex, it may sometimes happen that it becomes +hard to track which parameter value belongs with which placeholder. Did you +intend to pass this numeric value as `$7`, or as `$8`? The answer may depend +on an `if` that happened earlier in a different function. + +(Generally if things get that complex, it's a good idea to look for simpler +solutions. But especially when performance matters, sometimes you can't avoid +complexity like that.) + +There's a little helper class called `placeholders`. You can use it as a +counter which produces those placeholder strings, `$1`, `$2`, `$3`, et cetera. +When you start generating a complex statement, you can create both a `params` +and a `placeholders`: + + pqxx::params values; + pqxx::placeholders name; + +Let's say you've got some complex code to generate the conditions for an SQL +"WHERE" clause. You'll generally want to do these things close together in +your, so that you don't accidentally update one part and forget another: + + if (extra_clause) + { + // Extend the query text, using the current placeholder. + query += " AND x = " + name.get(); + // Add the parameter value. + values.append(my_x); + // Move on to the next placeholder value. + name.next(); + } + +Depending on the starting value of `name`, this might add to `query` a fragment +like "` AND x = $3`" or "` AND x = $5`". diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/performance.md b/ext/libpqxx-7.7.3/include/pqxx/doc/performance.md new file mode 100644 index 000000000..6c403684f --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/performance.md @@ -0,0 +1,24 @@ +Performance features {#performance} +==================== + +If your program's database interaction is not as efficient as it needs to be, +the first place to look is usually the SQL you're executing. But libpqxx +has a few specialized features to help you squeeze more performance out +of how you issue commands and retrieve data: + +* @ref streams. Use these as a faster way to transfer data between your + code and the database. +* `std::string_view` and `pqxx::zview`. In places where traditional C++ worked + with `std::string`, see whether `std::string_view` or `pqxx::zview` will + do. Of course that means that you'll have to look at the data's lifetime + more carefully, but it'll save the computer a lot of copying. +* @ref prepared. These can be executed many times without the server + parsing and planning them anew each time. They also save you having to + escape string parameters. +* `pqxx::pipeline` lets you send queries to the database in batches, and + continue other processing while they are executing. +* `pqxx::connecting` lets you start setting up a database connection, but + without blocking the thread. + +As always of course, don't risk the quality of your code for optimizations +that you don't need! diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/prepared-statement.md b/ext/libpqxx-7.7.3/include/pqxx/doc/prepared-statement.md new file mode 100644 index 000000000..5193866a6 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/prepared-statement.md @@ -0,0 +1,125 @@ +Prepared statements {#prepared} +=================== + +Prepared statements are SQL queries that you define once and then invoke +as many times as you like, typically with varying parameters. It's basically +a function that you can define ad hoc. + +If you have an SQL statement that you're going to execute many times in +quick succession, it may be more efficient to prepare it once and reuse it. +This saves the database backend the effort of parsing complex SQL and +figuring out an efficient execution plan. Another nice side effect is that +you don't need to worry about escaping parameters. Some corporate coding +standards require all SQL parameters to be passed in this way, to reduce the +risk of programmer mistakes leaving room for SQL injections. + + +Preparing a statement +--------------------- + +You create a prepared statement by preparing it on the connection (using the +`pqxx::connection::prepare` functions), passing an identifier and its SQL text. + +The identifier is the name by which the prepared statement will be known; it +should consist of ASCII letters, digits, and underscores only, and start with +an ASCII letter. The name is case-sensitive. + +```cxx + void prepare_my_statement(pqxx::connection &c) + { + c.prepare( + "my_statement", + "SELECT * FROM Employee WHERE name = 'Xavier'"); + } +``` + +Once you've done this, you'll be able to call `my_statement` from any +transaction you execute on the same connection. For this, use the +`pqxx::transaction_base::exec_prepared` functions. + +```cxx + pqxx::result execute_my_statement(pqxx::transaction_base &t) + { + return t.exec_prepared("my_statement"); + } +``` + + +Parameters +---------- + +Did I mention that prepared statements can have parameters? The query text +can contain `$1`, `$2` etc. as placeholders for parameter values that you +will provide when you invoke the prepared satement. + +See @ref parameters for more about this. And here's a simple example of +preparing a statement and invoking it with parameters: + +```cxx + void prepare_find(pqxx::connection &c) + { + // Prepare a statement called "find" that looks for employees with a + // given name (parameter 1) whose salary exceeds a given number + // (parameter 2). + c.prepare( + "find", + "SELECT * FROM Employee WHERE name = $1 AND salary > $2"); + } +``` + +This example looks up the prepared statement "find," passes `name` and +`min_salary` as parameters, and invokes the statement with those values: + +```cxx + pqxx::result execute_find( + pqxx::transaction_base &t, std::string name, int min_salary) + { + return t.exec_prepared("find", name, min_salary); + } +``` + + +A special prepared statement +---------------------------- + +There is one special case: the _nameless_ prepared statement. You may prepare +a statement without a name, i.e. whose name is an empty string. The unnamed +statement can be redefined at any time, without un-preparing it first. + + +Performance note +---------------- + +Don't assume that using prepared statements will speed up your application. +There are cases where prepared statements are actually slower than plain SQL. + +The reason is that the backend can often produce a better execution plan when +it knows the statement's actual parameter values. + +For example, say you've got a web application and you're querying for users +with status "inactive" who have email addresses in a given domain name X. If +X is a very popular provider, the best way for the database engine to plan the +query may be to list the inactive users first and then filter for the email +addresses you're looking for. But in other cases, it may be much faster to +find matching email addresses first and then see which of their owners are +"inactive." A prepared statement must be planned to fit either case, but a +direct query will be optimised based on table statistics, partial indexes, etc. + + +Zero bytes +---------- + +@warning Beware of "nul" bytes! + +Any string you pass as a parameter will end at the _first char with value +zero._ If you pass a string that contains a zero byte, the last byte in the +value will be the one just before the zero. + +So, if you need a zero byte in a string, consider that it's really a _binary +string,_ which is not the same thing as a text string. SQL represents binary +data as the `BYTEA` type, or in binary large objects ("blobs"). + +In libpqxx, you represent binary data as a range of `std::byte`. They must be +contiguous in memory, so that libpqxx can pass pointers to the underlying C +library. So you might use `std::basic_string`, or +`std::basic_string_view`, or `std::vector`. diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/streams.md b/ext/libpqxx-7.7.3/include/pqxx/doc/streams.md new file mode 100644 index 000000000..3df4d6126 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/streams.md @@ -0,0 +1,107 @@ +Streams {#streams} +======= + +Most of the time it's fine to retrieve data from the database using `SELECT` +queries, and store data using `INSERT`. But for those cases where efficiency +matters, there are two classes to help you do this better: `stream_from` and +`stream_to`. They're less flexible than SQL queries, and there's the risk of +losing your connection while you're in mid-stream, but you get some speed and +memory efficiencies in return. + +Both stream classes do data conversion for you: `stream_from` receives values +from the database in PostgreSQL's text format, and converts them to the C++ +types you specify. Likewise, `stream_to` converts C++ values you provide to +PostgreSQL's text format for transfer. (On its end, the database of course +converts values to and from their SQL types.) + + +Null values +----------- + +So how do you deal with nulls? It depends on the C++ type you're using. Some +types may have a built-in null value. For instance, if you have a +`char const *` value and you convert it to an SQL string, then converting a +`nullptr` will produce a NULL SQL value. + +But what do you do about C++ types which don't have a built-in null value, such +as `int`? The trick is to wrap it in `std::optional`. The difference between +`int` and `std::optional` is that the former always has an `int` value, +and the latter doesn't have to. + +Actually it's not just `std::optional`. You can do the same thing with +`std::unique_ptr` or `std::shared_ptr`. A smart pointer is less efficient than +`std::optional` in most situations because they allocate their value on the +heap, but sometimes that's what you want in order to save moving or copying +large values around. + +This part is not generic though. It won't work with just any smart-pointer +type, just the ones which are explicitly supported: `shared_ptr` and +`unique_ptr`. If you really need to, you can build support for additional +wrappers and smart pointers by copying the implementation patterns from the +existing smart-pointer support. + + +stream\_from +------------ + +Use `stream_from` to read data directly from the database. It's faster than +the transaction's `exec` functions if the result contains enough rows. But +also, you won't need to keep your full result set in memory. That can really +matter with larger data sets. + +And, you can start processing your data right after the first row of data comes +in from the server. With `exec()` you need to wait to receive all data, and +then you begin processing. With `stream_from` you can be processing data on +the client side while the server is still sending you the rest. + +You don't actually need to create a `stream_from` object yourself, though you +can. Two shorthand functions, @ref pqxx::transaction_base::stream +and @ref pqxx::transaction_base::for_each, can create the streams for you with +a minimum of overhead. + +Not all kinds of queries will work in a stream. Internally the streams make +use of PostgreSQL's `COPY` command, so see the PostgreSQL documentation for +`COPY` for the exact limitations. Basic `SELECT` and `UPDATE ... RETURNING` +queries should just work. + +As you read a row, the stream converts its fields to a tuple type containing +the value types you ask for: + + auto stream pqxx::stream_from::query( + tx, "SELECT name, points FROM score"); + std::tuple row; + while (stream >> row) + process(row); + stream.complete(); + +As the stream reads each row, it converts that row's data into your tuple, +goes through your loop body, and then promptly forgets that row's data. This +means you can easily process more data than will fit in memory. + + +stream\_to +---------- + +Use `stream_to` to write data directly to a database table. This saves you +having to perform an `INSERT` for every row, and so it can be significantly +faster if you want to insert more than just one or two rows at a time. + +As with `stream_from`, you can specify the table and the columns, and not much +else. You insert tuple-like objects of your choice: + + pqxx::stream_to stream{ + tx, + "score", + std::vector{"name", "points"}}; + for (auto const &entry: scores) + stream << entry; + stream.complete(); + +Each row is processed as you provide it, and not retained in memory after that. + +The call to `complete()` is more important here than it is for `stream_from`. +It's a lot like a "commit" or "abort" at the end of a transaction. If you omit +it, it will be done automatically during the stream's destructor. But since +destructors can't throw exceptions, any failures at that stage won't be visible +in your code. So, always call `complete()` on a `stream_to` to close it off +properly! diff --git a/ext/libpqxx-7.7.3/include/pqxx/doc/thread-safety.md b/ext/libpqxx-7.7.3/include/pqxx/doc/thread-safety.md new file mode 100644 index 000000000..07c7f9984 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/doc/thread-safety.md @@ -0,0 +1,29 @@ +Thread safety {#thread-safety} +============= + +This library does not contain any locking code to protect objects against +simultaneous modification in multi-threaded programs. Therefore it is up +to you, the user of the library, to ensure that your threaded client +programs perform no conflicting operations concurrently. + +Most of the time this isn't hard. Result sets are immutable, so you can +share them between threads without problem. The main rule is: + +@li Treat a connection, together with any and all objects related to it, as +a "world" of its own. You should generally make sure that the same "world" +is never accessed by another thread while you're doing anything non-const +in there. + +That means: don't issue a query on a transaction while you're also opening +a subtransaction, don't access a cursor while you may also be committing, +and so on. + +In particular, cursors are tricky. It's easy to perform a non-const +operation without noticing. So, if you're going to share cursors or +cursor-related objects between threads, lock very conservatively! + +Use `pqxx::describe_thread_safety` to find out at runtime what level of +thread safety is implemented in your build and version of libpqxx. It +returns a `pqxx::thread_safety_model` describing what you can and cannot rely +on. A command-line utility `tools/pqxxthreadsafety` prints out the same +information. diff --git a/ext/libpqxx-7.7.3/include/pqxx/errorhandler b/ext/libpqxx-7.7.3/include/pqxx/errorhandler new file mode 100644 index 000000000..ea572ee79 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/errorhandler @@ -0,0 +1,8 @@ +/** pqxx::errorhandler class. + * + * Callbacks for handling errors and warnings. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/errorhandler.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/errorhandler.hxx b/ext/libpqxx-7.7.3/include/pqxx/errorhandler.hxx new file mode 100644 index 000000000..2ffb5703c --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/errorhandler.hxx @@ -0,0 +1,92 @@ +/* Definition of the pqxx::errorhandler class. + * + * pqxx::errorhandler handlers errors and warnings in a database session. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/errorhandler instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ERRORHANDLER +#define PQXX_H_ERRORHANDLER + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/types.hxx" + + +namespace pqxx::internal::gate +{ +class errorhandler_connection; +} + + +namespace pqxx +{ +/** + * @addtogroup errorhandler + */ +//@{ + +/// Base class for error-handler callbacks. +/** To receive errors and warnings from a connection, subclass this with your + * own error-handler functor, and instantiate it for the connection. Destroying + * the handler un-registers it. + * + * A connection can have multiple error handlers at the same time. When the + * database connection emits an error or warning message, it passes the message + * to each error handler, starting with the most recently registered one and + * progressing towards the oldest one. However an error handler may also + * instruct the connection not to pass the message to further handlers by + * returning "false." + * + * @warning Strange things happen when a result object outlives its parent + * connection. If you register an error handler on a connection, then you must + * not access the result after destroying the connection. This applies even if + * you destroy the error handler first! + */ +class PQXX_LIBEXPORT errorhandler +{ +public: + explicit errorhandler(connection &); + virtual ~errorhandler(); + + /// Define in subclass: receive an error or warning message from the + /// database. + /** + * @return Whether the same error message should also be passed to the + * remaining, older errorhandlers. + */ + virtual bool operator()(char const msg[]) noexcept = 0; + + errorhandler() = delete; + errorhandler(errorhandler const &) = delete; + errorhandler &operator=(errorhandler const &) = delete; + +private: + connection *m_home; + + friend class internal::gate::errorhandler_connection; + void unregister() noexcept; +}; + + +/// An error handler that suppresses any previously registered error handlers. +class quiet_errorhandler : public errorhandler +{ +public: + /// Suppress error notices. + quiet_errorhandler(connection &conn) : errorhandler{conn} {} + + /// Revert to previous handling of error notices. + virtual bool operator()(char const[]) noexcept override { return false; } +}; + +//@} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/except b/ext/libpqxx-7.7.3/include/pqxx/except new file mode 100644 index 000000000..e5dd508bf --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/except @@ -0,0 +1,8 @@ +/** libpqxx exception classes. + * + * pqxx::sql_error, pqxx::broken_connection, pqxx::in_doubt_error, ... + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/except.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/except.hxx b/ext/libpqxx-7.7.3/include/pqxx/except.hxx new file mode 100644 index 000000000..24f959437 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/except.hxx @@ -0,0 +1,447 @@ +/* Definition of libpqxx exception classes. + * + * pqxx::sql_error, pqxx::broken_connection, pqxx::in_doubt_error, ... + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/except instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_EXCEPT +#define PQXX_H_EXCEPT + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + + +namespace pqxx +{ +/** + * @addtogroup exception Exception classes + * + * These exception classes follow, roughly, the two-level hierarchy defined by + * the PostgreSQL SQLSTATE error codes (see Appendix A of the PostgreSQL + * documentation corresponding to your server version). This is not a complete + * mapping though. There are other differences as well, e.g. the error code + * for `statement_completion_unknown` has a separate status in libpqxx as + * @ref in_doubt_error, and `too_many_connections` is classified as a + * `broken_connection` rather than a subtype of `insufficient_resources`. + * + * @see http://www.postgresql.org/docs/9.4/interactive/errcodes-appendix.html + * + * @{ + */ + +/// Run-time failure encountered by libpqxx, similar to std::runtime_error. +struct PQXX_LIBEXPORT failure : std::runtime_error +{ + explicit failure(std::string const &); +}; + + +/// Exception class for lost or failed backend connection. +/** + * @warning When this happens on Unix-like systems, you may also get a SIGPIPE + * signal. That signal aborts the program by default, so if you wish to be + * able to continue after a connection breaks, be sure to disarm this signal. + * + * If you're working on a Unix-like system, see the manual page for + * `signal` (2) on how to deal with SIGPIPE. The easiest way to make this + * signal harmless is to make your program ignore it: + * + * ```cxx + * #include + * + * int main() + * { + * signal(SIGPIPE, SIG_IGN); + * // ... + * ``` + */ +struct PQXX_LIBEXPORT broken_connection : failure +{ + broken_connection(); + explicit broken_connection(std::string const &); +}; + + +/// The caller attempted to set a variable to null, which is not allowed. +struct PQXX_LIBEXPORT variable_set_to_null : failure +{ + variable_set_to_null(); + explicit variable_set_to_null(std::string const &); +}; + + +/// Exception class for failed queries. +/** Carries, in addition to a regular error message, a copy of the failed query + * and (if available) the SQLSTATE value accompanying the error. + */ +class PQXX_LIBEXPORT sql_error : public failure +{ + /// Query string. Empty if unknown. + std::string const m_query; + /// SQLSTATE string describing the error type, if known; or empty string. + std::string const m_sqlstate; + +public: + explicit sql_error( + std::string const &whatarg = "", std::string const &Q = "", + char const sqlstate[] = nullptr); + virtual ~sql_error() noexcept override; + + /// The query whose execution triggered the exception + [[nodiscard]] PQXX_PURE std::string const &query() const noexcept; + + /// SQLSTATE error code if known, or empty string otherwise. + [[nodiscard]] PQXX_PURE std::string const &sqlstate() const noexcept; +}; + + +/// "Help, I don't know whether transaction was committed successfully!" +/** Exception that might be thrown in rare cases where the connection to the + * database is lost while finishing a database transaction, and there's no way + * of telling whether it was actually executed by the backend. In this case + * the database is left in an indeterminate (but consistent) state, and only + * manual inspection will tell which is the case. + */ +struct PQXX_LIBEXPORT in_doubt_error : failure +{ + explicit in_doubt_error(std::string const &); +}; + + +/// The backend saw itself forced to roll back the ongoing transaction. +struct PQXX_LIBEXPORT transaction_rollback : sql_error +{ + explicit transaction_rollback( + std::string const &whatarg, std::string const &q = "", + char const sqlstate[] = nullptr); +}; + + +/// Transaction failed to serialize. Please retry it. +/** Can only happen at transaction isolation levels REPEATABLE READ and + * SERIALIZABLE. + * + * The current transaction cannot be committed without violating the guarantees + * made by its isolation level. This is the effect of a conflict with another + * ongoing transaction. The transaction may still succeed if you try to + * perform it again. + */ +struct PQXX_LIBEXPORT serialization_failure : transaction_rollback +{ + explicit serialization_failure( + std::string const &whatarg, std::string const &q, + char const sqlstate[] = nullptr); +}; + + +/// We can't tell whether our last statement succeeded. +struct PQXX_LIBEXPORT statement_completion_unknown : transaction_rollback +{ + explicit statement_completion_unknown( + std::string const &whatarg, std::string const &q, + char const sqlstate[] = nullptr); +}; + + +/// The ongoing transaction has deadlocked. Retrying it may help. +struct PQXX_LIBEXPORT deadlock_detected : transaction_rollback +{ + explicit deadlock_detected( + std::string const &whatarg, std::string const &q, + char const sqlstate[] = nullptr); +}; + + +/// Internal error in libpqxx library +struct PQXX_LIBEXPORT internal_error : std::logic_error +{ + explicit internal_error(std::string const &); +}; + + +/// Error in usage of libpqxx library, similar to std::logic_error +struct PQXX_LIBEXPORT usage_error : std::logic_error +{ + explicit usage_error(std::string const &); +}; + + +/// Invalid argument passed to libpqxx, similar to std::invalid_argument +struct PQXX_LIBEXPORT argument_error : std::invalid_argument +{ + explicit argument_error(std::string const &); +}; + + +/// Value conversion failed, e.g. when converting "Hello" to int. +struct PQXX_LIBEXPORT conversion_error : std::domain_error +{ + explicit conversion_error(std::string const &); +}; + + +/// Could not convert value to string: not enough buffer space. +struct PQXX_LIBEXPORT conversion_overrun : conversion_error +{ + explicit conversion_overrun(std::string const &); +}; + + +/// Something is out of range, similar to std::out_of_range +struct PQXX_LIBEXPORT range_error : std::out_of_range +{ + explicit range_error(std::string const &); +}; + + +/// Query returned an unexpected number of rows. +struct PQXX_LIBEXPORT unexpected_rows : public range_error +{ + explicit unexpected_rows(std::string const &msg) : range_error{msg} {} +}; + + +/// Database feature not supported in current setup. +struct PQXX_LIBEXPORT feature_not_supported : sql_error +{ + explicit feature_not_supported( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +/// Error in data provided to SQL statement. +struct PQXX_LIBEXPORT data_exception : sql_error +{ + explicit data_exception( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT integrity_constraint_violation : sql_error +{ + explicit integrity_constraint_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT restrict_violation : integrity_constraint_violation +{ + explicit restrict_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + integrity_constraint_violation{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT not_null_violation : integrity_constraint_violation +{ + explicit not_null_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + integrity_constraint_violation{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT foreign_key_violation : integrity_constraint_violation +{ + explicit foreign_key_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + integrity_constraint_violation{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT unique_violation : integrity_constraint_violation +{ + explicit unique_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + integrity_constraint_violation{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT check_violation : integrity_constraint_violation +{ + explicit check_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + integrity_constraint_violation{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT invalid_cursor_state : sql_error +{ + explicit invalid_cursor_state( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT invalid_sql_statement_name : sql_error +{ + explicit invalid_sql_statement_name( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT invalid_cursor_name : sql_error +{ + explicit invalid_cursor_name( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT syntax_error : sql_error +{ + /// Approximate position in string where error occurred, or -1 if unknown. + int const error_position; + + explicit syntax_error( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, int pos = -1) : + sql_error{err, Q, sqlstate}, error_position{pos} + {} +}; + +struct PQXX_LIBEXPORT undefined_column : syntax_error +{ + explicit undefined_column( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + syntax_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT undefined_function : syntax_error +{ + explicit undefined_function( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + syntax_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT undefined_table : syntax_error +{ + explicit undefined_table( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + syntax_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT insufficient_privilege : sql_error +{ + explicit insufficient_privilege( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +/// Resource shortage on the server +struct PQXX_LIBEXPORT insufficient_resources : sql_error +{ + explicit insufficient_resources( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT disk_full : insufficient_resources +{ + explicit disk_full( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + insufficient_resources{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT out_of_memory : insufficient_resources +{ + explicit out_of_memory( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + insufficient_resources{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT too_many_connections : broken_connection +{ + explicit too_many_connections(std::string const &err) : + broken_connection{err} + {} +}; + +/// PL/pgSQL error +/** Exceptions derived from this class are errors from PL/pgSQL procedures. + */ +struct PQXX_LIBEXPORT plpgsql_error : sql_error +{ + explicit plpgsql_error( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +/// Exception raised in PL/pgSQL procedure +struct PQXX_LIBEXPORT plpgsql_raise : plpgsql_error +{ + explicit plpgsql_raise( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + plpgsql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT plpgsql_no_data_found : plpgsql_error +{ + explicit plpgsql_no_data_found( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + plpgsql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT plpgsql_too_many_rows : plpgsql_error +{ + explicit plpgsql_too_many_rows( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + plpgsql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT blob_already_exists : failure +{ + explicit blob_already_exists(std::string const &); +}; + +/** + * @} + */ +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/field b/ext/libpqxx-7.7.3/include/pqxx/field new file mode 100644 index 000000000..37cb69e84 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/field @@ -0,0 +1,8 @@ +/** pqxx::field class. + * + * pqxx::field refers to a field in a query result. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/field.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/field.hxx b/ext/libpqxx-7.7.3/include/pqxx/field.hxx new file mode 100644 index 000000000..b8b869fe4 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/field.hxx @@ -0,0 +1,542 @@ +/* Definitions for the pqxx::field class. + * + * pqxx::field refers to a field in a query result. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/field instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_FIELD +#define PQXX_H_FIELD + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#include "pqxx/array.hxx" +#include "pqxx/composite.hxx" +#include "pqxx/result.hxx" +#include "pqxx/strconv.hxx" +#include "pqxx/types.hxx" + +namespace pqxx +{ +/// Reference to a field in a result set. +/** A field represents one entry in a row. It represents an actual value + * in the result set, and can be converted to various types. + */ +class PQXX_LIBEXPORT field +{ +public: + using size_type = field_size_type; + + /// Constructor. Do not call this yourself; libpqxx will do it for you. + /** Create field as reference to a field in a result set. + * @param r Row that this field is part of. + * @param c Column number of this field. + */ + [[deprecated( + "Do not construct fields yourself. Get them from the row.")]] field(row const &r, row_size_type c) noexcept; + + /// Constructor. Do not call this yourself; libpqxx will do it for you. + [[deprecated( + "Do not construct fields yourself. Get them from the " + "row.")]] field() noexcept = default; + + /** + * @name Comparison + */ + //@{ + // TODO: noexcept. Breaks ABI. + /// Byte-by-byte comparison of two fields (all nulls are considered equal) + /** @warning null handling is still open to discussion and change! + * + * Handling of null values differs from that in SQL where a comparison + * involving a null value yields null, so nulls are never considered equal + * to one another or even to themselves. + * + * Null handling also probably differs from the closest equivalent in C++, + * which is the NaN (Not-a-Number) value, a singularity comparable to + * SQL's null. This is because the builtin == operator demands that a == a. + * + * The usefulness of this operator is questionable. No interpretation + * whatsoever is imposed on the data; 0 and 0.0 are considered different, + * as are null vs. the empty string, or even different (but possibly + * equivalent and equally valid) encodings of the same Unicode character + * etc. + */ + [[nodiscard]] PQXX_PURE bool operator==(field const &) const; + + /// Byte-by-byte comparison (all nulls are considered equal) + /** @warning See operator==() for important information about this operator + */ + [[nodiscard]] PQXX_PURE bool operator!=(field const &rhs) const noexcept + { + return not operator==(rhs); + } + //@} + + /** + * @name Column information + */ + //@{ + /// Column name. + [[nodiscard]] PQXX_PURE char const *name() const &; + + /// Column type. + [[nodiscard]] oid PQXX_PURE type() const; + + /// What table did this column come from? + [[nodiscard]] PQXX_PURE oid table() const; + + /// Return row number. The first row is row 0, the second is row 1, etc. + PQXX_PURE constexpr row_size_type num() const noexcept { return col(); } + + /// What column number in its originating table did this column come from? + [[nodiscard]] PQXX_PURE row_size_type table_column() const; + //@} + + /** + * @name Content access + */ + //@{ + /// Read as `string_view`, or an empty one if null. + /** The result only remains usable while the data for the underlying + * @ref result exists. Once all `result` objects referring to that data have + * been destroyed, the `string_view` will no longer point to valid memory. + */ + [[nodiscard]] PQXX_PURE std::string_view view() const & + { + return std::string_view(c_str(), size()); + } + + /// Read as plain C string. + /** Since the field's data is stored internally in the form of a + * zero-terminated C string, this is the fastest way to read it. Use the + * to() or as() functions to convert the string to other types such as + * `int`, or to C++ strings. + * + * Do not use this for BYTEA values, or other binary values. To read those, + * convert the value to your desired type using `to()` or `as()`. For + * example: `f.as>()`. + */ + [[nodiscard]] PQXX_PURE char const *c_str() const &; + + /// Is this field's value null? + [[nodiscard]] PQXX_PURE bool is_null() const noexcept; + + /// Return number of bytes taken up by the field's value. + [[nodiscard]] PQXX_PURE size_type size() const noexcept; + + /// Read value into obj; or if null, leave obj untouched and return `false`. + /** This can be used with optional types (except pointers other than C-style + * strings). + */ + template + auto to(T &obj) const -> typename std::enable_if_t< + (not std::is_pointer::value or std::is_same::value), + bool> + { + if (is_null()) + { + return false; + } + else + { + auto const bytes{c_str()}; + from_string(bytes, obj); + return true; + } + } + + /// Read field as a composite value, write its components into `fields`. + /** @warning This is still experimental. It may change or be replaced. + * + * Returns whether the field was null. If it was, it will not touch the + * values in `fields`. + */ + template bool composite_to(T &...fields) const + { + if (is_null()) + { + return false; + } + else + { + parse_composite(m_home.m_encoding, view(), fields...); + return true; + } + } + + /// Read value into obj; or leave obj untouched and return `false` if null. + template bool operator>>(T &obj) const { return to(obj); } + + /// Read value into obj; or if null, use default value and return `false`. + /** This can be used with `std::optional`, as well as with standard smart + * pointer types, but not with raw pointers. If the conversion from a + * PostgreSQL string representation allocates a pointer (e.g. using `new`), + * then the object's later deallocation should be baked in as well, right + * from the point where the object is created. So if you want a pointer, use + * a smart pointer, not a raw pointer. + * + * There is one exception, of course: C-style strings. Those are just + * pointers to the field's internal text data. + */ + template + auto to(T &obj, T const &default_value) const -> typename std::enable_if_t< + (not std::is_pointer::value or std::is_same::value), + bool> + { + bool const null{is_null()}; + if (null) + obj = default_value; + else + obj = from_string(this->view()); + return not null; + } + + /// Return value as object of given type, or default value if null. + /** Note that unless the function is instantiated with an explicit template + * argument, the Default value's type also determines the result type. + */ + template T as(T const &default_value) const + { + if (is_null()) + return default_value; + else + return from_string(this->view()); + } + + /// Return value as object of given type, or throw exception if null. + /** Use as `as>()` or `as()` as + * an alternative to `get()`; this is disabled for use with raw pointers + * (other than C-strings) because storage for the value can't safely be + * allocated here + */ + template T as() const + { + if (is_null()) + { + if constexpr (not nullness::has_null) + internal::throw_null_conversion(type_name); + else + return nullness::null(); + } + else + { + return from_string(this->view()); + } + } + + /// Return value wrapped in some optional type (empty for nulls). + /** Use as `get()` as before to obtain previous behavior, or specify + * container type with `get()` + */ + template class O = std::optional> + constexpr O get() const + { + return as>(); + } + + // TODO: constexpr noexcept, once array_parser constructor gets those. + /// Parse the field as an SQL array. + /** Call the parser to retrieve values (and structure) from the array. + * + * Make sure the @ref result object stays alive until parsing is finished. If + * you keep the @ref row of `field` object alive, it will keep the @ref + * result object alive as well. + */ + array_parser as_array() const & + { + return array_parser{c_str(), m_home.m_encoding}; + } + //@} + + +protected: + constexpr result const &home() const noexcept { return m_home; } + constexpr result::size_type idx() const noexcept { return m_row; } + constexpr row_size_type col() const noexcept { return m_col; } + + // TODO: Create gates. + friend class pqxx::result; + friend class pqxx::row; + field( + result const &r, result_size_type row_num, row_size_type col_num) noexcept + : + m_col{col_num}, m_home{r}, m_row{row_num} + {} + + /** + * You'd expect this to be unsigned, but due to the way reverse iterators + * are related to regular iterators, it must be allowed to underflow to -1. + */ + row_size_type m_col; + +private: + result m_home; + result::size_type m_row; +}; + + +template<> inline bool field::to(std::string &obj) const +{ + bool const null{is_null()}; + if (not null) + obj = std::string{view()}; + return not null; +} + + +template<> +inline bool field::to( + std::string &obj, std::string const &default_value) const +{ + bool const null{is_null()}; + if (null) + obj = default_value; + else + obj = std::string{view()}; + return not null; +} + + +/// Specialization: `to(char const *&)`. +/** The buffer has the same lifetime as the data in this result (i.e. of this + * result object, or the last remaining one copied from it etc.), so take care + * not to use it after the last result object referring to this query result is + * destroyed. + */ +template<> inline bool field::to(char const *&obj) const +{ + bool const null{is_null()}; + if (not null) + obj = c_str(); + return not null; +} + + +template<> inline bool field::to(std::string_view &obj) const +{ + bool const null{is_null()}; + if (not null) + obj = view(); + return not null; +} + + +template<> +inline bool field::to( + std::string_view &obj, std::string_view const &default_value) const +{ + bool const null{is_null()}; + if (null) + obj = default_value; + else + obj = view(); + return not null; +} + + +template<> inline std::string_view field::as() const +{ + if (is_null()) + PQXX_UNLIKELY + internal::throw_null_conversion(type_name); + return view(); +} + + +template<> +inline std::string_view +field::as(std::string_view const &default_value) const +{ + return is_null() ? default_value : view(); +} + + +template<> inline bool field::to(zview &obj) const +{ + bool const null{is_null()}; + if (not null) + obj = zview{c_str(), size()}; + return not null; +} + + +template<> +inline bool field::to(zview &obj, zview const &default_value) const +{ + bool const null{is_null()}; + if (null) + obj = default_value; + else + obj = zview{c_str(), size()}; + return not null; +} + + +template<> inline zview field::as() const +{ + if (is_null()) + PQXX_UNLIKELY + internal::throw_null_conversion(type_name); + return zview{c_str(), size()}; +} + + +template<> inline zview field::as(zview const &default_value) const +{ + return is_null() ? default_value : zview{c_str(), size()}; +} + + +template> +class field_streambuf : public std::basic_streambuf +{ +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + using openmode = std::ios::openmode; + using seekdir = std::ios::seekdir; + + explicit field_streambuf(field const &f) : m_field{f} { initialize(); } + +protected: + virtual int sync() override { return traits_type::eof(); } + + virtual pos_type seekoff(off_type, seekdir, openmode) override + { + return traits_type::eof(); + } + virtual pos_type seekpos(pos_type, openmode) override + { + return traits_type::eof(); + } + virtual int_type overflow(int_type) override { return traits_type::eof(); } + virtual int_type underflow() override { return traits_type::eof(); } + +private: + field const &m_field; + + int_type initialize() + { + auto g{static_cast(const_cast(m_field.c_str()))}; + this->setg(g, g, g + std::size(m_field)); + return int_type(std::size(m_field)); + } +}; + + +/// Input stream that gets its data from a result field +/** Use this class exactly as you would any other istream to read data from a + * field. All formatting and streaming operations of `std::istream` are + * supported. What you'll typically want to use, however, is the fieldstream + * alias (which defines a @ref basic_fieldstream for `char`). This is similar + * to how e.g. `std::ifstream` relates to `std::basic_ifstream`. + * + * This class has only been tested for the char type (and its default traits). + */ +template> +class basic_fieldstream : public std::basic_istream +{ + using super = std::basic_istream; + +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + + basic_fieldstream(field const &f) : super{nullptr}, m_buf{f} + { + super::init(&m_buf); + } + +private: + field_streambuf m_buf; +}; + +using fieldstream = basic_fieldstream; + +/// Write a result field to any type of stream +/** This can be convenient when writing a field to an output stream. More + * importantly, it lets you write a field to e.g. a `stringstream` which you + * can then use to read, format and convert the field in ways that to() does + * not support. + * + * Example: parse a field into a variable of the nonstandard + * "long long" type. + * + * ```cxx + * extern result R; + * long long L; + * stringstream S; + * + * // Write field's string into S + * S << R[0][0]; + * + * // Parse contents of S into L + * S >> L; + * ``` + */ +template +inline std::basic_ostream & +operator<<(std::basic_ostream &s, field const &value) +{ + s.write(value.c_str(), std::streamsize(std::size(value))); + return s; +} + + +/// Convert a field's value to type `T`. +/** Unlike the "regular" `from_string`, this knows how to deal with null + * values. + */ +template inline T from_string(field const &value) +{ + if (value.is_null()) + { + if constexpr (nullness::has_null) + return nullness::null(); + else + internal::throw_null_conversion(type_name); + } + else + { + return from_string(value.view()); + } +} + + +/// Convert a field's value to `nullptr_t`. +/** Yes, you read that right. This conversion does nothing useful. It always + * returns `nullptr`. + * + * Except... what if the field is not null? In that case, this throws + * @ref conversion_error. + */ +template<> +inline std::nullptr_t from_string(field const &value) +{ + if (not value.is_null()) + throw conversion_error{ + "Extracting non-null field into nullptr_t variable."}; + return nullptr; +} + + +/// Convert a field to a string. +template<> PQXX_LIBEXPORT std::string to_string(field const &value); +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/array-composite.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/array-composite.hxx new file mode 100644 index 000000000..d2b6603e5 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/array-composite.hxx @@ -0,0 +1,305 @@ +#if !defined(PQXX_ARRAY_COMPOSITE_HXX) +# define PQXX_ARRAY_COMPOSITE_HXX + +# include + +# include "pqxx/strconv.hxx" + +namespace pqxx::internal +{ +// Find the end of a double-quoted string. +/** `input[pos]` must be the opening double quote. + * + * Returns the offset of the first position after the closing quote. + */ +inline std::size_t scan_double_quoted_string( + char const input[], std::size_t size, std::size_t pos, + pqxx::internal::glyph_scanner_func *scan) +{ + // XXX: find_char<'"', '\\'>(). + auto next{scan(input, size, pos)}; + bool at_quote{false}; + for (pos = next, next = scan(input, size, pos); pos < size; + pos = next, next = scan(input, size, pos)) + { + if (at_quote) + { + if (next - pos == 1 and input[pos] == '"') + { + // We just read a pair of double quotes. Carry on. + at_quote = false; + } + else + { + // We just read one double quote, and now we're at a character that's + // not a second double quote. Ergo, that last character was the + // closing double quote and this is the position right after it. + return pos; + } + } + else if (next - pos == 1) + { + switch (input[pos]) + { + case '\\': + // Backslash escape. Skip ahead by one more character. + pos = next; + next = scan(input, size, pos); + break; + + case '"': + // This is either the closing double quote, or the first of a pair of + // double quotes. + at_quote = true; + break; + } + } + else + { + // Multibyte character. Carry on. + } + } + if (not at_quote) + throw argument_error{ + "Missing closing double-quote: " + std::string{input}}; + return pos; +} + + +/// Un-quote and un-escape a double-quoted SQL string. +inline std::string parse_double_quoted_string( + char const input[], std::size_t end, std::size_t pos, + pqxx::internal::glyph_scanner_func *scan) +{ + std::string output; + // Maximum output size is same as the input size, minus the opening and + // closing quotes. Or in the extreme opposite case, the real number could be + // half that. Usually it'll be a pretty close estimate. + output.reserve(std::size_t(end - pos - 2)); + + for (auto here{scan(input, end, pos)}, next{scan(input, end, here)}; + here < end - 1; here = next, next = scan(input, end, here)) + { + // A backslash here is always an escape. So is a double-quote, since we're + // inside the double-quoted string. In either case, we can just ignore the + // escape character and use the next character. This is the one redeeming + // feature of SQL's escaping system. + if ((next - here == 1) and (input[here] == '\\' or input[here] == '"')) + { + // Skip escape. + here = next; + next = scan(input, end, here); + } + output.append(input + here, input + next); + } + return output; +} + + +/// Find the end of an unquoted string in an array or composite-type value. +/** Stops when it gets to the end of the input; or when it sees any of the + * characters in STOP which has not been escaped. + * + * For array values, STOP is a comma, a semicolon, or a closing brace. For + * a value of a composite type, STOP is a comma or a closing parenthesis. + */ +template +inline std::size_t scan_unquoted_string( + char const input[], std::size_t size, std::size_t pos, + pqxx::internal::glyph_scanner_func *scan) +{ + bool at_backslash{false}; + auto next{scan(input, size, pos)}; + while ((pos < size) and + ((next - pos) > 1 or at_backslash or ((input[pos] != STOP) and ...))) + { + pos = next; + next = scan(input, size, pos); + at_backslash = + ((not at_backslash) and ((next - pos) == 1) and (input[pos] == '\\')); + } + return pos; +} + + +/// Parse an unquoted array entry or cfield of a composite-type field. +inline std::string parse_unquoted_string( + char const input[], std::size_t end, std::size_t pos, + pqxx::internal::glyph_scanner_func *scan) +{ + std::string output; + bool at_backslash{false}; + output.reserve(end - pos); + for (auto next{scan(input, end, pos)}; pos < end; + pos = next, next = scan(input, end, pos)) + { + at_backslash = + ((not at_backslash) and ((next - pos) == 1) and (input[pos] == '\\')); + if (not at_backslash) + output.append(input + pos, next - pos); + } + return output; +} + + +/// Parse a field of a composite-type value. +/** `T` is the C++ type of the field we're parsing, and `index` is its + * zero-based number. + * + * Strip off the leading parenthesis or bracket yourself before parsing. + * However, this function will parse the lcosing parenthesis or bracket. + * + * After a successful parse, `pos` will point at `std::end(text)`. + * + * For the purposes of parsing, ranges and arrays count as compositve values, + * so this function supports parsing those. If you specifically need a closing + * parenthesis, check afterwards that `text` did not end in a bracket instead. + * + * @param index Index of the current field, zero-based. It will increment for + * the next field. + * @param input Full input text for the entire composite-type value. + * @param pos Starting position (in `input`) of the field that we're parsing. + * After parsing, this will point at the beginning of the next field if + * there is one, or one position past the last character otherwise. + * @param field Destination for the parsed value. + * @param scan Glyph scanning function for the relevant encoding type. + * @param last_field Number of the last field in the value (zero-based). When + * parsing the last field, this will equal `index`. + */ +template +inline void parse_composite_field( + std::size_t &index, std::string_view input, std::size_t &pos, T &field, + glyph_scanner_func *scan, std::size_t last_field) +{ + assert(index <= last_field); + auto next{scan(std::data(input), std::size(input), pos)}; + if ((next - pos) != 1) + throw conversion_error{"Non-ASCII character in composite-type syntax."}; + + // Expect a field. + switch (input[pos]) + { + case ',': + case ')': + case ']': + // The field is empty, i.e, null. + if constexpr (nullness::has_null) + field = nullness::null(); + else + throw conversion_error{ + "Can't read composite field " + to_string(index) + ": C++ type " + + type_name + " does not support nulls."}; + break; + + case '"': { + auto const stop{scan_double_quoted_string( + std::data(input), std::size(input), pos, scan)}; + auto const text{ + parse_double_quoted_string(std::data(input), stop, pos, scan)}; + field = from_string(text); + pos = stop; + } + break; + + default: { + auto const stop{scan_unquoted_string<',', ')', ']'>( + std::data(input), std::size(input), pos, scan)}; + auto const text{parse_unquoted_string(std::data(input), stop, pos, scan)}; + field = from_string(text); + pos = stop; + } + break; + } + + // Expect a comma or a closing parenthesis. + next = scan(std::data(input), std::size(input), pos); + + if ((next - pos) != 1) + throw conversion_error{ + "Unexpected non-ASCII character after composite field: " + + std::string{input}}; + + if (index < last_field) + { + if (input[pos] != ',') + throw conversion_error{ + "Found '" + std::string{input[pos]} + + "' in composite value where comma was expected: " + std::data(input)}; + } + else + { + if (input[pos] == ',') + throw conversion_error{ + "Composite value contained more fields than the expected " + + to_string(last_field) + ": " + std::data(input)}; + if (input[pos] != ')' and input[pos] != ']') + throw conversion_error{ + "Composite value has unexpected characters where closing parenthesis " + "was expected: " + + std::string{input}}; + if (next != std::size(input)) + throw conversion_error{ + "Composite value has unexpected text after closing parenthesis: " + + std::string{input}}; + } + + pos = next; + ++index; +} + + +/// Conservatively estimate buffer size needed for a composite field. +template +inline std::size_t size_composite_field_buffer(T const &field) +{ + if constexpr (is_unquoted_safe) + { + // Safe to copy, without quotes or escaping. Drop the terminating zero. + return size_buffer(field) - 1; + } + else + { + // + Opening quote. + // + Field budget. + // - Terminating zero. + // + Escaping for each byte in the field's string representation. + // - Escaping for terminating zero. + // + Closing quote. + return 1 + 2 * (size_buffer(field) - 1) + 1; + } +} + + +template +inline void write_composite_field(char *&pos, char *end, T const &field) +{ + if constexpr (is_unquoted_safe) + { + // No need for quoting or escaping. Convert it straight into its final + // place in the buffer, and "backspace" the trailing zero. + pos = string_traits::into_buf(pos, end, field) - 1; + } + else + { + // The field may need escaping, which means we need an intermediate buffer. + // To avoid allocating that at run time, we use the end of the buffer that + // we have. + auto const budget{size_buffer(field)}; + *pos++ = '"'; + + // Now escape buf into its final position. + for (char const c : string_traits::to_buf(end - budget, end, field)) + { + if ((c == '"') or (c == '\\')) + *pos++ = '\\'; + + *pos++ = c; + } + + *pos++ = '"'; + } + + *pos++ = ','; +} +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/callgate.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/callgate.hxx new file mode 100644 index 000000000..42f7703e3 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/callgate.hxx @@ -0,0 +1,70 @@ +#ifndef PQXX_H_CALLGATE +#define PQXX_H_CALLGATE + +/* +Here's what a typical gate class definition looks like: + +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE @gateclass@ : callgate<@host@> +{ + friend class @client@; + + @gateclass@(reference x) : super(x) {} + + // Methods here. Use home() to access the host-class object. +}; +} // namespace pqxx::internal::gate +*/ + +namespace pqxx::internal +{ +/// Base class for call gates. +/** + * A call gate defines a limited, private interface on the host class that + * specified client classes can access. + * + * The metaphor works as follows: the gate stands in front of a "home," which + * is really a class, and only lets specific friends in. + * + * To implement a call gate that gives client C access to host H, + * * derive a gate class from callgate; + * * make the gate class a friend of H; + * * make C a friend of the gate class; and + * * implement "stuff C can do with H" as private members in the gate class. + * + * This special kind of "gated" friendship gives C private access to H, but + * only through an expressly limited interface. The gate class can access its + * host object as home(). + * + * Keep gate classes entirely stateless. They should be ultra-lightweight + * wrappers for their host classes, and be optimized away as much as possible + * by the compiler. Once you start adding state, you're on a slippery slope + * away from the pure, clean, limited interface pattern that gate classes are + * meant to implement. + * + * Ideally, all member functions of the gate class should be one-liners passing + * calls straight on to the host class. It can be useful however to break this + * rule temporarily during inter-class refactoring. + */ +template class PQXX_PRIVATE callgate +{ +protected: + /// This class, to keep constructors easy. + using super = callgate; + /// A reference to the host class. Helps keep constructors easy. + using reference = HOME &; + + callgate(reference x) : m_home(x) {} + + /// The home object. The gate class has full "private" access. + reference home() const noexcept { return m_home; } + +private: + reference m_home; +}; +} // namespace pqxx::internal + +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/concat.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/concat.hxx new file mode 100644 index 000000000..cd28bde7c --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/concat.hxx @@ -0,0 +1,45 @@ +#if !defined(PQXX_CONCAT_HXX) +# define PQXX_CONCAT_HXX + +# include +# include + +# include "pqxx/strconv.hxx" + +namespace pqxx::internal +{ +/// Convert item to a string, write it into [here, end). +template +void render_item(TYPE const &item, char *&here, char *end) +{ + here = string_traits::into_buf(here, end, item) - 1; +} + + +// C++20: Support non-random_access_range ranges. +/// Efficiently combine a bunch of items into one big string. +/** Use this as an optimised version of string concatentation. It takes just + * about any type; it will represent each item as a string according to its + * @ref string_traits. + * + * This is a simpler, more specialised version of @ref separated_list for a + * statically known series of items, possibly of different types. + */ +template +[[nodiscard]] inline std::string concat(TYPE... item) +{ + std::string buf; + // Size to accommodate string representations of all inputs, minus their + // terminating zero bytes. + buf.resize(size_buffer(item...)); + + char *const data{buf.data()}; + char *here = data; + char *end = data + std::size(buf); + (render_item(item, here, end), ...); + + buf.resize(static_cast(here - data)); + return buf; +} +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/conversions.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/conversions.hxx new file mode 100644 index 000000000..1df4fdead --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/conversions.hxx @@ -0,0 +1,1188 @@ +#include +#include +#include +#include +#include +#include + +#if defined(PQXX_HAVE_SPAN) && __has_include() +# include +#endif + +#include +#include +#include + +#include "pqxx/types.hxx" +#include "pqxx/util.hxx" + + +/* Internal helpers for string conversion, and conversion implementations. + * + * Do not include this header directly. The libpqxx headers do it for you. + */ +namespace pqxx::internal +{ +/// Convert a number in [0, 9] to its ASCII digit. +inline constexpr char number_to_digit(int i) noexcept +{ + return static_cast(i + '0'); +} + + +/// Compute numeric value of given textual digit (assuming that it is a digit). +constexpr int digit_to_number(char c) noexcept +{ + return c - '0'; +} + + +/// Summarize buffer overrun. +/** Don't worry about the exact parameter types: the sizes will be reasonably + * small, and nonnegative. + */ +std::string PQXX_LIBEXPORT +state_buffer_overrun(int have_bytes, int need_bytes); + + +template +inline std::string state_buffer_overrun(HAVE have_bytes, NEED need_bytes) +{ + return state_buffer_overrun( + static_cast(have_bytes), static_cast(need_bytes)); +} + + +/// Throw exception for attempt to convert null to given type. +[[noreturn]] PQXX_LIBEXPORT void +throw_null_conversion(std::string const &type); + + +/// Deliberately nonfunctional conversion traits for `char` types. +/** There are no string conversions for `char` and its signed and unsigned + * variants. Such a conversion would be dangerously ambiguous: should we treat + * it as text, or as a small integer? It'd be an open invitation for bugs. + * + * But the error message when you get this wrong is very cryptic. So, we + * derive dummy @ref string_traits implementations from this dummy type, and + * ensure that the compiler disallows their use. The compiler error message + * will at least contain a hint of the root of the problem. + */ +template struct disallowed_ambiguous_char_conversion +{ + static char *into_buf(char *, char *, CHAR_TYPE) = delete; + static constexpr zview + to_buf(char *, char *, CHAR_TYPE const &) noexcept = delete; + + static constexpr std::size_t + size_buffer(CHAR_TYPE const &) noexcept = delete; + static CHAR_TYPE from_string(std::string_view) = delete; +}; + + +template PQXX_LIBEXPORT extern std::string to_string_float(T); + + +/// Generic implementation for into_buf, on top of to_buf. +template +inline char *generic_into_buf(char *begin, char *end, T const &value) +{ + zview const text{string_traits::to_buf(begin, end, value)}; + auto const space{end - begin}; + // Include the trailing zero. + auto const len = std::size(text) + 1; + if (internal::cmp_greater(len, space)) + throw conversion_overrun{ + "Not enough buffer space to insert " + type_name + ". " + + state_buffer_overrun(space, len)}; + std::memmove(begin, text.data(), len); + return begin + len; +} + + +/// String traits for builtin integral types (though not bool). +template struct integral_traits +{ + static PQXX_LIBEXPORT T from_string(std::string_view text); + static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value); + static PQXX_LIBEXPORT char *into_buf(char *begin, char *end, T const &value); + + static constexpr std::size_t size_buffer(T const &) noexcept + { + /** Includes a sign if needed; the number of base-10 digits which the type + * can reliably represent; the one extra base-10 digit which the type can + * only partially represent; and the terminating zero. + */ + return std::is_signed_v + std::numeric_limits::digits10 + 1 + 1; + } +}; + + +/// String traits for builtin floating-point types. +template struct float_traits +{ + static PQXX_LIBEXPORT T from_string(std::string_view text); + static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value); + static PQXX_LIBEXPORT char *into_buf(char *begin, char *end, T const &value); + + // Return a nonnegative integral value's number of decimal digits. + static constexpr std::size_t digits10(std::size_t value) noexcept + { + if (value < 10) + return 1; + else + return 1 + digits10(value / 10); + } + + static constexpr std::size_t size_buffer(T const &) noexcept + { + using lims = std::numeric_limits; + // See #328 for a detailed discussion on the maximum number of digits. + // + // In a nutshell: for the big cases, the scientific notation is always + // the shortest one, and therefore the one that to_chars will pick. + // + // So... How long can the scientific notation get? 1 (for sign) + 1 (for + // decimal point) + 1 (for 'e') + 1 (for exponent sign) + max_digits10 + + // max number of digits in the exponent + 1 (terminating zero). + // + // What's the max number of digits in the exponent? It's the max number of + // digits out of the most negative exponent and the most positive one. + // + // The longest positive exponent is easy: 1 + ceil(log10(max_exponent10)). + // (The extra 1 is because 10^n takes up 1 + n digits, not n.) + // + // The longest negative exponent is a bit harder: min_exponent10 gives us + // the smallest power of 10 which a normalised version of T can represent. + // But the smallest denormalised power of 10 that T can represent is + // another max_digits10 powers of 10 below that. + // needs a minus sign. + // + // All this stuff messes with my head a bit because it's on the order of + // log10(log10(n)). It's easy to get the number of logs wrong. + auto const max_pos_exp{digits10(lims::max_exponent10)}; + // Really want std::abs(lims::min_exponent10), but MSVC 2017 apparently has + // problems with std::abs. So we use -lims::min_exponent10 instead. + auto const max_neg_exp{ + digits10(lims::max_digits10 - lims::min_exponent10)}; + return 1 + // Sign. + 1 + // Decimal point. + std::numeric_limits::max_digits10 + // Mantissa digits. + 1 + // Exponent "e". + 1 + // Exponent sign. + // Spell this weirdly to stop Windows compilers from reading this as + // a call to their "max" macro when NOMINMAX is not defined. + (std::max)(max_pos_exp, max_neg_exp) + // Exponent digits. + 1; // Terminating zero. + } +}; +} // namespace pqxx::internal + + +namespace pqxx +{ +/// The built-in arithmetic types do not have inherent null values. +template +struct nullness>> : no_null +{}; + + +template<> struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> +struct string_traits + : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> +struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> +struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> +struct string_traits + : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> struct string_traits : internal::float_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> struct string_traits : internal::float_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> +struct string_traits : internal::float_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; + + +template<> struct string_traits +{ + static PQXX_LIBEXPORT bool from_string(std::string_view text); + + static constexpr zview to_buf(char *, char *, bool const &value) noexcept + { + return value ? "true"_zv : "false"_zv; + } + + static char *into_buf(char *begin, char *end, bool const &value) + { + return pqxx::internal::generic_into_buf(begin, end, value); + } + + static constexpr std::size_t size_buffer(bool const &) noexcept { return 6; } +}; + + +/// We don't support conversion to/from `char` types. +/** Why are these disallowed? Because they are ambiguous. It's not inherently + * clear whether we should treat values of these types as text or as small + * integers. Either choice would lead to bugs. + */ +template<> +struct string_traits + : internal::disallowed_ambiguous_char_conversion +{}; + +/// We don't support conversion to/from `char` types. +/** Why are these disallowed? Because they are ambiguous. It's not inherently + * clear whether we should treat values of these types as text or as small + * integers. Either choice would lead to bugs. + */ +template<> +struct string_traits + : internal::disallowed_ambiguous_char_conversion +{}; + +/// We don't support conversion to/from `char` types. +/** Why are these disallowed? Because they are ambiguous. It's not inherently + * clear whether we should treat values of these types as text or as small + * integers. Either choice would lead to bugs. + */ +template<> +struct string_traits + : internal::disallowed_ambiguous_char_conversion +{}; + + +template<> inline constexpr bool is_unquoted_safe{true}; + + +template struct nullness> +{ + static constexpr bool has_null = true; + /// Technically, you could have an optional of an always-null type. + static constexpr bool always_null = nullness::always_null; + static constexpr bool is_null(std::optional const &v) noexcept + { + return ((not v.has_value()) or pqxx::is_null(*v)); + } + static constexpr std::optional null() { return {}; } +}; + + +template +inline constexpr format param_format(std::optional const &value) +{ + return param_format(*value); +} + + +template struct string_traits> +{ + static char *into_buf(char *begin, char *end, std::optional const &value) + { + return string_traits::into_buf(begin, end, *value); + } + + static zview to_buf(char *begin, char *end, std::optional const &value) + { + if (value.has_value()) + return string_traits::to_buf(begin, end, *value); + else + return {}; + } + + static std::optional from_string(std::string_view text) + { + return std::optional{ + std::in_place, string_traits::from_string(text)}; + } + + static std::size_t size_buffer(std::optional const &value) noexcept + { + return pqxx::size_buffer(value.value()); + } +}; + + +template +inline constexpr bool is_unquoted_safe>{is_unquoted_safe}; + + +template struct nullness> +{ + static constexpr bool has_null = (nullness::has_null or ...); + static constexpr bool always_null = (nullness::always_null and ...); + static constexpr bool is_null(std::variant const &value) noexcept + { + return std::visit( + [](auto const &i) noexcept { + return nullness>::is_null(i); + }, + value); + } + + // We don't support `null()` for `std::variant`. + /** It would be technically possible to have a `null` in the case where just + * one of the types has a null, but it gets complicated and arbitrary. + */ + static constexpr std::variant null() = delete; +}; + + +template struct string_traits> +{ + static char * + into_buf(char *begin, char *end, std::variant const &value) + { + return std::visit( + [begin, end](auto const &i) { + return string_traits>::into_buf(begin, end, i); + }, + value); + } + static zview to_buf(char *begin, char *end, std::variant const &value) + { + return std::visit( + [begin, end](auto const &i) { + return string_traits>::to_buf(begin, end, i); + }, + value); + } + static std::size_t size_buffer(std::variant const &value) noexcept + { + return std::visit( + [](auto const &i) noexcept { return pqxx::size_buffer(i); }, value); + } + + /** There's no from_string for std::variant. We could have one with a rule + * like "pick the first type which fits the value," but we'd have to look + * into how natural that API feels to users. + */ + static std::variant from_string(std::string_view) = delete; +}; + + +template +inline constexpr format param_format(std::variant const &value) +{ + return std::visit([](auto &v) { return param_format(v); }, value); +} + + +template +inline constexpr bool is_unquoted_safe>{ + (is_unquoted_safe and ...)}; + + +template inline T from_string(std::stringstream const &text) +{ + return from_string(text.str()); +} + + +template<> struct string_traits +{ + static char *into_buf(char *, char *, std::nullptr_t) = delete; + + static constexpr zview + to_buf(char *, char *, std::nullptr_t const &) noexcept + { + return {}; + } + + static constexpr std::size_t size_buffer(std::nullptr_t = nullptr) noexcept + { + return 0; + } + static std::nullptr_t from_string(std::string_view) = delete; +}; + + +template<> struct string_traits +{ + static char *into_buf(char *, char *, std::nullopt_t) = delete; + + static constexpr zview + to_buf(char *, char *, std::nullopt_t const &) noexcept + { + return {}; + } + + static constexpr std::size_t size_buffer(std::nullopt_t) noexcept + { + return 0; + } + static std::nullopt_t from_string(std::string_view) = delete; +}; + + +template<> struct string_traits +{ + static char *into_buf(char *, char *, std::monostate) = delete; + + static constexpr zview + to_buf(char *, char *, std::monostate const &) noexcept + { + return {}; + } + + static constexpr std::size_t size_buffer(std::monostate) noexcept + { + return 0; + } + static std::monostate from_string(std::string_view) = delete; +}; + + +template<> inline constexpr bool is_unquoted_safe{true}; + + +template<> struct nullness +{ + static constexpr bool has_null = true; + static constexpr bool always_null = false; + static constexpr bool is_null(char const *t) noexcept + { + return t == nullptr; + } + static constexpr char const *null() noexcept { return nullptr; } +}; + + +/// String traits for C-style string ("pointer to char const"). +template<> struct string_traits +{ + static char const *from_string(std::string_view text) { return text.data(); } + + static zview to_buf(char *begin, char *end, char const *const &value) + { + return generic_to_buf(begin, end, value); + } + + static char *into_buf(char *begin, char *end, char const *const &value) + { + auto const space{end - begin}; + // Count the trailing zero, even though std::strlen() and friends don't. + auto const len{std::strlen(value) + 1}; + if (space < ptrdiff_t(len)) + throw conversion_overrun{ + "Could not copy string: buffer too small. " + + pqxx::internal::state_buffer_overrun(space, len)}; + std::memmove(begin, value, len); + return begin + len; + } + + static std::size_t size_buffer(char const *const &value) noexcept + { + return std::strlen(value) + 1; + } +}; + + +template<> struct nullness +{ + static constexpr bool has_null = true; + static constexpr bool always_null = false; + static constexpr bool is_null(char const *t) noexcept + { + return t == nullptr; + } + static constexpr char const *null() { return nullptr; } +}; + + +/// String traits for non-const C-style string ("pointer to char"). +template<> struct string_traits +{ + static char *into_buf(char *begin, char *end, char *const &value) + { + return string_traits::into_buf(begin, end, value); + } + static zview to_buf(char *begin, char *end, char *const &value) + { + return string_traits::to_buf(begin, end, value); + } + static std::size_t size_buffer(char *const &value) noexcept + { + return string_traits::size_buffer(value); + } + + /// Don't allow conversion to this type since it breaks const-safety. + static char *from_string(std::string_view) = delete; +}; + + +template struct nullness : no_null +{}; + + +/// String traits for C-style string constant ("array of char"). +/** @warning This assumes that every array-of-char is a C-style string literal. + * So, it must include a trailing zero. and it must have static duration. + */ +template struct string_traits +{ + static constexpr zview + to_buf(char *, char *, char const (&value)[N]) noexcept + { + return zview{value, N - 1}; + } + + static char *into_buf(char *begin, char *end, char const (&value)[N]) + { + if (internal::cmp_less(end - begin, size_buffer(value))) + throw conversion_overrun{ + "Could not convert char[] to string: too long for buffer."}; + std::memcpy(begin, value, N); + return begin + N; + } + static constexpr std::size_t size_buffer(char const (&)[N]) noexcept + { + return N; + } + + /// Don't allow conversion to this type. + static void from_string(std::string_view) = delete; +}; + + +template<> struct nullness : no_null +{}; + + +template<> struct string_traits +{ + static std::string from_string(std::string_view text) + { + return std::string{text}; + } + + static char *into_buf(char *begin, char *end, std::string const &value) + { + if (internal::cmp_greater_equal(std::size(value), end - begin)) + throw conversion_overrun{ + "Could not convert string to string: too long for buffer."}; + // Include the trailing zero. + value.copy(begin, std::size(value)); + begin[std::size(value)] = '\0'; + return begin + std::size(value) + 1; + } + + static zview to_buf(char *begin, char *end, std::string const &value) + { + return generic_to_buf(begin, end, value); + } + + static std::size_t size_buffer(std::string const &value) noexcept + { + return std::size(value) + 1; + } +}; + + +/// There's no real null for `std::string_view`. +/** I'm not sure how clear-cut this is: a `string_view` may have a null + * data pointer, which is analogous to a null `char` pointer. + */ +template<> struct nullness : no_null +{}; + + +/// String traits for `string_view`. +template<> struct string_traits +{ + static constexpr std::size_t + size_buffer(std::string_view const &value) noexcept + { + return std::size(value) + 1; + } + + static char *into_buf(char *begin, char *end, std::string_view const &value) + { + if (internal::cmp_greater_equal(std::size(value), end - begin)) + throw conversion_overrun{ + "Could not store string_view: too long for buffer."}; + value.copy(begin, std::size(value)); + begin[std::size(value)] = '\0'; + return begin + std::size(value) + 1; + } + + /// Don't convert to this type; it has nowhere to store its contents. + static std::string_view from_string(std::string_view) = delete; +}; + + +template<> struct nullness : no_null +{}; + + +/// String traits for `zview`. +template<> struct string_traits +{ + static constexpr std::size_t + size_buffer(std::string_view const &value) noexcept + { + return std::size(value) + 1; + } + + static char *into_buf(char *begin, char *end, zview const &value) + { + auto const size{std::size(value)}; + if (internal::cmp_less_equal(end - begin, std::size(value))) + throw conversion_overrun{"Not enough buffer space to store this zview."}; + value.copy(begin, size); + begin[size] = '\0'; + return begin + size + 1; + } + + static std::string_view to_buf(char *begin, char *end, zview const &value) + { + return {into_buf(begin, end, value), std::size(value)}; + } + + /// Don't convert to this type; it has nowhere to store its contents. + static zview from_string(std::string_view) = delete; +}; + + +template<> struct nullness : no_null +{}; + + +template<> struct string_traits +{ + static std::size_t size_buffer(std::stringstream const &) = delete; + + static std::stringstream from_string(std::string_view text) + { + std::stringstream stream; + stream.write(text.data(), std::streamsize(std::size(text))); + return stream; + } + + static char *into_buf(char *, char *, std::stringstream const &) = delete; + static std::string_view + to_buf(char *, char *, std::stringstream const &) = delete; +}; + + +template<> struct nullness +{ + static constexpr bool has_null = true; + static constexpr bool always_null = true; + static constexpr bool is_null(std::nullptr_t const &) noexcept + { + return true; + } + static constexpr std::nullptr_t null() noexcept { return nullptr; } +}; + + +template<> struct nullness +{ + static constexpr bool has_null = true; + static constexpr bool always_null = true; + static constexpr bool is_null(std::nullopt_t const &) noexcept + { + return true; + } + static constexpr std::nullopt_t null() noexcept { return std::nullopt; } +}; + + +template<> struct nullness +{ + static constexpr bool has_null = true; + static constexpr bool always_null = true; + static constexpr bool is_null(std::monostate const &) noexcept + { + return true; + } + static constexpr std::monostate null() noexcept { return {}; } +}; + + +template struct nullness> +{ + static constexpr bool has_null = true; + static constexpr bool always_null = false; + static constexpr bool is_null(std::unique_ptr const &t) noexcept + { + return not t or pqxx::is_null(*t); + } + static constexpr std::unique_ptr null() { return {}; } +}; + + +template +struct string_traits> +{ + static std::unique_ptr from_string(std::string_view text) + { + return std::make_unique(string_traits::from_string(text)); + } + + static char * + into_buf(char *begin, char *end, std::unique_ptr const &value) + { + return string_traits::into_buf(begin, end, *value); + } + + static zview + to_buf(char *begin, char *end, std::unique_ptr const &value) + { + if (value) + return string_traits::to_buf(begin, end, *value); + else + return {}; + } + + static std::size_t + size_buffer(std::unique_ptr const &value) noexcept + { + return pqxx::size_buffer(*value.get()); + } +}; + + +template +inline format param_format(std::unique_ptr const &value) +{ + return param_format(*value); +} + + +template +inline constexpr bool is_unquoted_safe>{ + is_unquoted_safe}; + + +template struct nullness> +{ + static constexpr bool has_null = true; + static constexpr bool always_null = false; + static constexpr bool is_null(std::shared_ptr const &t) noexcept + { + return not t or pqxx::is_null(*t); + } + static constexpr std::shared_ptr null() { return {}; } +}; + + +template struct string_traits> +{ + static std::shared_ptr from_string(std::string_view text) + { + return std::make_shared(string_traits::from_string(text)); + } + + static zview to_buf(char *begin, char *end, std::shared_ptr const &value) + { + return string_traits::to_buf(begin, end, *value); + } + static char * + into_buf(char *begin, char *end, std::shared_ptr const &value) + { + return string_traits::into_buf(begin, end, *value); + } + static std::size_t size_buffer(std::shared_ptr const &value) noexcept + { + return pqxx::size_buffer(*value); + } +}; + + +template format param_format(std::shared_ptr const &value) +{ + return param_format(*value); +} + + +template +inline constexpr bool is_unquoted_safe>{ + is_unquoted_safe}; + + +template<> +struct nullness> + : no_null> +{}; + + +#if defined(PQXX_HAVE_CONCEPTS) +template struct nullness : no_null +{}; + + +template inline constexpr format param_format(DATA const &) +{ + return format::binary; +} + + +template struct string_traits +{ + static std::size_t size_buffer(DATA const &value) noexcept + { + return internal::size_esc_bin(std::size(value)); + } + + static zview to_buf(char *begin, char *end, DATA const &value) + { + return generic_to_buf(begin, end, value); + } + + static char *into_buf(char *begin, char *end, DATA const &value) + { + auto const budget{size_buffer(value)}; + if (internal::cmp_less(end - begin, budget)) + throw conversion_overrun{ + "Not enough buffer space to escape binary data."}; + internal::esc_bin(value, begin); + return begin + budget; + } + + static DATA from_string(std::string_view text) + { + auto const size{pqxx::internal::size_unesc_bin(std::size(text))}; + std::basic_string buf; + buf.resize(size); + pqxx::internal::unesc_bin(text, reinterpret_cast(buf.data())); + return buf; + } +}; +#endif // PQXX_HAVE_CONCEPTS + + +template<> struct string_traits> +{ + static std::size_t + size_buffer(std::basic_string const &value) noexcept + { + return internal::size_esc_bin(std::size(value)); + } + + static zview + to_buf(char *begin, char *end, std::basic_string const &value) + { + return generic_to_buf(begin, end, value); + } + + static char * + into_buf(char *begin, char *end, std::basic_string const &value) + { + auto const budget{size_buffer(value)}; + if (internal::cmp_less(end - begin, budget)) + throw conversion_overrun{ + "Not enough buffer space to escape binary data."}; + internal::esc_bin(value, begin); + return begin + budget; + } + + static std::basic_string from_string(std::string_view text) + { + auto const size{pqxx::internal::size_unesc_bin(std::size(text))}; + std::basic_string buf; + buf.resize(size); + pqxx::internal::unesc_bin(text, reinterpret_cast(buf.data())); + return buf; + } +}; + + +template<> +inline constexpr format param_format(std::basic_string const &) +{ + return format::binary; +} + + +template<> +struct nullness> + : no_null> +{}; + + +template<> struct string_traits> +{ + static std::size_t + size_buffer(std::basic_string_view const &value) noexcept + { + return internal::size_esc_bin(std::size(value)); + } + + static zview to_buf( + char *begin, char *end, std::basic_string_view const &value) + { + return generic_to_buf(begin, end, value); + } + + static char *into_buf( + char *begin, char *end, std::basic_string_view const &value) + { + auto const budget{size_buffer(value)}; + if (internal::cmp_less(end - begin, budget)) + throw conversion_overrun{ + "Not enough buffer space to escape binary data."}; + internal::esc_bin(value, begin); + return begin + budget; + } + + // There's no from_string, because there's nobody to hold the data. +}; + +template<> +inline constexpr format param_format(std::basic_string_view const &) +{ + return format::binary; +} +} // namespace pqxx + + +namespace pqxx::internal +{ +/// String traits for SQL arrays. +template struct array_string_traits +{ +private: + using elt_type = strip_t>; + using elt_traits = string_traits; + static constexpr zview s_null{"NULL"}; + +public: + static zview to_buf(char *begin, char *end, Container const &value) + { + return generic_to_buf(begin, end, value); + } + + static char *into_buf(char *begin, char *end, Container const &value) + { + std::size_t const budget{size_buffer(value)}; + if (internal::cmp_less(end - begin, budget)) + throw conversion_overrun{ + "Not enough buffer space to convert array to string."}; + + char *here = begin; + *here++ = '{'; + + bool nonempty{false}; + for (auto const &elt : value) + { + if (is_null(elt)) + { + s_null.copy(here, std::size(s_null)); + here += std::size(s_null); + } + else if constexpr (is_sql_array) + { + // Render nested array in-place. Then erase the trailing zero. + here = elt_traits::into_buf(here, end, elt) - 1; + } + else if constexpr (is_unquoted_safe) + { + // No need to quote or escape. Just convert the value straight into + // its place in the array, and "backspace" the trailing zero. + here = elt_traits::into_buf(here, end, elt) - 1; + } + else + { + *here++ = '"'; + + // Use the tail end of the destination buffer as an intermediate + // buffer. + auto const elt_budget{pqxx::size_buffer(elt)}; + for (char const c : elt_traits::to_buf(end - elt_budget, end, elt)) + { + if (c == '\\' or c == '"') + *here++ = '\\'; + *here++ = c; + } + *here++ = '"'; + } + *here++ = array_separator; + nonempty = true; + } + + // Erase that last comma, if present. + if (nonempty) + here--; + + *here++ = '}'; + *here++ = '\0'; + + return here; + } + + static std::size_t size_buffer(Container const &value) noexcept + { + if constexpr (is_unquoted_safe) + return 3 + std::accumulate( + std::begin(value), std::end(value), std::size_t{}, + [](std::size_t acc, elt_type const &elt) { + return acc + + (pqxx::is_null(elt) ? + std::size(s_null) : + elt_traits::size_buffer(elt)) - + 1; + }); + else + return 3 + std::accumulate( + std::begin(value), std::end(value), std::size_t{}, + [](std::size_t acc, elt_type const &elt) { + // Opening and closing quotes, plus worst-case escaping, + // but don't count the trailing zeroes. + std::size_t const elt_size{ + pqxx::is_null(elt) ? std::size(s_null) : + elt_traits::size_buffer(elt) - 1}; + return acc + 2 * elt_size + 2; + }); + } + + // We don't yet support parsing of array types using from_string. Doing so + // would require a reference to the connection. +}; +} // namespace pqxx::internal + + +namespace pqxx +{ +template +struct nullness> : no_null> +{}; + + +template +struct string_traits> + : internal::array_string_traits> +{}; + + +/// We don't know how to pass array params in binary format, so pass as text. +template +inline constexpr format param_format(std::vector const &) +{ + return format::text; +} + + +/// A `std::vector` is a binary string. Other vectors are not. +template +inline constexpr format param_format(std::vector const &) +{ + return format::binary; +} + + +template inline constexpr bool is_sql_array>{true}; + + +template +struct nullness> : no_null> +{}; + + +template +struct string_traits> + : internal::array_string_traits> +{}; + + +/// We don't know how to pass array params in binary format, so pass as text. +template +inline constexpr format param_format(std::array const &) +{ + return format::text; +} + + +/// An array of `std::byte` is a binary string. +template +inline constexpr format param_format(std::array const &) +{ + return format::binary; +} + + +template +inline constexpr bool is_sql_array>{true}; +} // namespace pqxx + + +namespace pqxx +{ +template inline std::string to_string(T const &value) +{ + if (is_null(value)) + throw conversion_error{ + "Attempt to convert null " + type_name + " to a string."}; + + std::string buf; + // We can't just reserve() space; modifying the terminating zero leads to + // undefined behaviour. + buf.resize(size_buffer(value)); + auto const data{buf.data()}; + auto const end{ + string_traits::into_buf(data, data + std::size(buf), value)}; + buf.resize(static_cast(end - data - 1)); + return buf; +} + + +template<> inline std::string to_string(float const &value) +{ + return internal::to_string_float(value); +} +template<> inline std::string to_string(double const &value) +{ + return internal::to_string_float(value); +} +template<> inline std::string to_string(long double const &value) +{ + return internal::to_string_float(value); +} +template<> inline std::string to_string(std::stringstream const &value) +{ + return value.str(); +} + + +template inline void into_string(T const &value, std::string &out) +{ + if (is_null(value)) + throw conversion_error{ + "Attempt to convert null " + type_name + " to a string."}; + + // We can't just reserve() data; modifying the terminating zero leads to + // undefined behaviour. + out.resize(size_buffer(value) + 1); + auto const data{out.data()}; + auto const end{ + string_traits::into_buf(data, data + std::size(out), value)}; + out.resize(static_cast(end - data - 1)); +} +} // namespace pqxx diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/encoding_group.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/encoding_group.hxx new file mode 100644 index 000000000..e17736e5b --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/encoding_group.hxx @@ -0,0 +1,60 @@ +/** Enum type for supporting encodings in libpqxx + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ENCODING_GROUP +#define PQXX_H_ENCODING_GROUP + +#include + +namespace pqxx::internal +{ +// Types of encodings supported by PostgreSQL, see +// https://www.postgresql.org/docs/current/static/multibyte.html#CHARSET-TABLE +enum class encoding_group +{ + // Handles all single-byte fixed-width encodings + MONOBYTE, + + // Multibyte encodings. + // Many of these can embed ASCII-like bytes inside multibyte characters, + // notably Big5, SJIS, SHIFT_JIS_2004, GP18030, GBK, JOHAB, UHC. + BIG5, + EUC_CN, + // TODO: Merge EUC_JP and EUC_JIS_2004? + EUC_JP, + EUC_JIS_2004, + EUC_KR, + EUC_TW, + GB18030, + GBK, + JOHAB, + MULE_INTERNAL, + // TODO: Merge SJIS and SHIFT_JIS_2004? + SJIS, + SHIFT_JIS_2004, + UHC, + UTF8, +}; + + +// TODO:: Can we just use string_view now? +/// Function type: "find the end of the current glyph." +/** This type of function takes a text buffer, and a location in that buffer, + * and returns the location one byte past the end of the current glyph. + * + * The start offset marks the beginning of the current glyph. It must fall + * within the buffer. + * + * There are multiple different glyph scanner implementations, for different + * kinds of encodings. + */ +using glyph_scanner_func = + std::size_t(char const buffer[], std::size_t buffer_len, std::size_t start); +} // namespace pqxx::internal + +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/encodings.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/encodings.hxx new file mode 100644 index 000000000..ba7fecc70 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/encodings.hxx @@ -0,0 +1,90 @@ +/** Internal string encodings support for libpqxx + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ENCODINGS +#define PQXX_H_ENCODINGS + +#include "pqxx/internal/encoding_group.hxx" + +#include +#include + + +namespace pqxx::internal +{ +char const *name_encoding(int encoding_id); + +/// Convert libpq encoding enum or encoding name to its libpqxx group. +encoding_group enc_group(int /* libpq encoding ID */); +encoding_group enc_group(std::string_view); + + +/// Look up the glyph scanner function for a given encoding group. +/** To identify the glyph boundaries in a buffer, call this to obtain the + * scanner function appropriate for the buffer's encoding. Then, repeatedly + * call the scanner function to find the glyphs. + */ +PQXX_LIBEXPORT glyph_scanner_func *get_glyph_scanner(encoding_group); + + +// TODO: For ASCII search, treat UTF8/EUC_*/MULE_INTERNAL as MONOBYTE. + +/// Find any of the ASCII characters `NEEDLE` in `haystack`. +/** Scans through `haystack` until it finds a single-byte character that + * matches any value in `NEEDLE`. + * + * If it finds one, returns its offset. If not, returns the end of the + * haystack. + */ +template +inline std::size_t find_char( + glyph_scanner_func *scanner, std::string_view haystack, + std::size_t here = 0u) +{ + auto const sz{std::size(haystack)}; + auto const data{std::data(haystack)}; + while (here < sz) + { + auto next{scanner(data, sz, here)}; + // (For some reason gcc had a problem with a right-fold here. But clang + // was fine.) + if ((... or (data[here] == NEEDLE))) + { + // Also check against a multibyte character starting with a bytes which + // just happens to match one of the ASCII bytes we're looking for. It'd + // be cleaner to check that first, but either works. So, let's apply the + // most selective filter first and skip this check in almost all cases. + if (next == here + 1) + return here; + } + + // Nope, no hit. Move on. + here = next; + } + return sz; +} + + +/// Iterate over the glyphs in a buffer. +/** Scans the glyphs in the buffer, and for each, passes its begin and its + * one-past-end pointers to `callback`. + */ +template +inline void for_glyphs( + encoding_group enc, CALLABLE callback, char const buffer[], + std::size_t buffer_len, std::size_t start = 0) +{ + auto const scan{get_glyph_scanner(enc)}; + for (std::size_t here = start, next; here < buffer_len; here = next) + { + next = scan(buffer, buffer_len, here); + callback(buffer + here, buffer + next); + } +} +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-errorhandler.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-errorhandler.hxx new file mode 100644 index 000000000..ffc12a6cf --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-errorhandler.hxx @@ -0,0 +1,26 @@ +#include + +namespace pqxx +{ +class connection; +class errorhandler; +} // namespace pqxx + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_errorhandler : callgate +{ + friend class pqxx::errorhandler; + + connection_errorhandler(reference x) : super(x) {} + + void register_errorhandler(errorhandler *h) + { + home().register_errorhandler(h); + } + void unregister_errorhandler(errorhandler *h) + { + home().unregister_errorhandler(h); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-largeobject.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-largeobject.hxx new file mode 100644 index 000000000..49feaf9e6 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-largeobject.hxx @@ -0,0 +1,35 @@ +#include + +#include +#include + +namespace pqxx +{ +class blob; +class largeobject; +} // namespace pqxx + + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_largeobject : callgate +{ + friend class pqxx::blob; + friend class pqxx::largeobject; + + connection_largeobject(reference x) : super(x) {} + + pq::PGconn *raw_connection() const { return home().raw_connection(); } +}; + + +class PQXX_PRIVATE const_connection_largeobject : callgate +{ + friend class pqxx::blob; + friend class pqxx::largeobject; + + const_connection_largeobject(reference x) : super(x) {} + + std::string error_message() const { return home().err_msg(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-notification_receiver.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-notification_receiver.hxx new file mode 100644 index 000000000..0bcb2db17 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-notification_receiver.hxx @@ -0,0 +1,29 @@ +#include + +#include "pqxx/connection.hxx" + + +namespace pqxx +{ +class notification_receiver; +} + + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_notification_receiver : callgate +{ + friend class pqxx::notification_receiver; + + connection_notification_receiver(reference x) : super(x) {} + + void add_receiver(notification_receiver *receiver) + { + home().add_receiver(receiver); + } + void remove_receiver(notification_receiver *receiver) noexcept + { + home().remove_receiver(receiver); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-pipeline.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-pipeline.hxx new file mode 100644 index 000000000..c6ae6e17a --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-pipeline.hxx @@ -0,0 +1,23 @@ +#include "pqxx/internal/libpq-forward.hxx" +#include + +#include "pqxx/pipeline.hxx" + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_pipeline : callgate +{ + friend class pqxx::pipeline; + + connection_pipeline(reference x) : super(x) {} + + void start_exec(char const query[]) { home().start_exec(query); } + pqxx::internal::pq::PGresult *get_result() { return home().get_result(); } + void cancel_query() { home().cancel_query(); } + + bool consume_input() noexcept { return home().consume_input(); } + bool is_busy() const noexcept { return home().is_busy(); } + + int encoding_id() { return home().encoding_id(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-sql_cursor.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-sql_cursor.hxx new file mode 100644 index 000000000..51a889844 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-sql_cursor.hxx @@ -0,0 +1,19 @@ +#include + +namespace pqxx::internal +{ +class sql_cursor; +} + + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_sql_cursor : callgate +{ + friend class pqxx::internal::sql_cursor; + + connection_sql_cursor(reference x) : super(x) {} + + result exec(char const query[]) { return home().exec(query); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-stream_from.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-stream_from.hxx new file mode 100644 index 000000000..8961e7146 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-stream_from.hxx @@ -0,0 +1,15 @@ +#include + +#include "pqxx/connection.hxx" + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_stream_from : callgate +{ + friend class pqxx::stream_from; + + connection_stream_from(reference x) : super{x} {} + + auto read_copy_line() { return home().read_copy_line(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-stream_to.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-stream_to.hxx new file mode 100644 index 000000000..a6974fb21 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-stream_to.hxx @@ -0,0 +1,17 @@ +#include + +#include "pqxx/stream_to.hxx" + + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_stream_to : callgate +{ + friend class pqxx::stream_to; + + connection_stream_to(reference x) : super(x) {} + + void write_copy_line(std::string_view line) { home().write_copy_line(line); } + void end_copy_write() { home().end_copy_write(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-transaction.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-transaction.hxx new file mode 100644 index 000000000..74d659253 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/connection-transaction.hxx @@ -0,0 +1,44 @@ +#include + +namespace pqxx +{ +class connection; +} + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_transaction : callgate +{ + friend class pqxx::transaction_base; + + connection_transaction(reference x) : super(x) {} + + template result exec(STRING query, std::string_view desc) + { + return home().exec(query, desc); + } + + void register_transaction(transaction_base *t) + { + home().register_transaction(t); + } + void unregister_transaction(transaction_base *t) noexcept + { + home().unregister_transaction(t); + } + + auto read_copy_line() { return home().read_copy_line(); } + void write_copy_line(std::string_view line) { home().write_copy_line(line); } + void end_copy_write() { home().end_copy_write(); } + + result exec_prepared(zview statement, internal::c_params const &args) + { + return home().exec_prepared(statement, args); + } + + result exec_params(zview query, internal::c_params const &args) + { + return home().exec_params(query, args); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/errorhandler-connection.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/errorhandler-connection.hxx new file mode 100644 index 000000000..5560cedec --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/errorhandler-connection.hxx @@ -0,0 +1,13 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE errorhandler_connection : callgate +{ + friend class pqxx::connection; + + errorhandler_connection(reference x) : super(x) {} + + void unregister() noexcept { home().unregister(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/icursor_iterator-icursorstream.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/icursor_iterator-icursorstream.hxx new file mode 100644 index 000000000..296d22145 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/icursor_iterator-icursorstream.hxx @@ -0,0 +1,24 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE icursor_iterator_icursorstream : callgate +{ + friend class pqxx::icursorstream; + + icursor_iterator_icursorstream(reference x) : super(x) {} + + icursor_iterator::difference_type pos() const noexcept + { + return home().pos(); + } + + icursor_iterator *get_prev() { return home().m_prev; } + void set_prev(icursor_iterator *i) { home().m_prev = i; } + + icursor_iterator *get_next() { return home().m_next; } + void set_next(icursor_iterator *i) { home().m_next = i; } + + void fill(result const &r) { home().fill(r); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/icursorstream-icursor_iterator.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/icursorstream-icursor_iterator.hxx new file mode 100644 index 000000000..56056d5ef --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/icursorstream-icursor_iterator.hxx @@ -0,0 +1,32 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE icursorstream_icursor_iterator : callgate +{ + friend class pqxx::icursor_iterator; + + icursorstream_icursor_iterator(reference x) : super(x) {} + + void insert_iterator(icursor_iterator *i) noexcept + { + home().insert_iterator(i); + } + + void remove_iterator(icursor_iterator *i) const noexcept + { + home().remove_iterator(i); + } + + icursorstream::size_type forward() { return home().forward(); } + icursorstream::size_type forward(icursorstream::size_type n) + { + return home().forward(n); + } + + void service_iterators(icursorstream::difference_type p) + { + home().service_iterators(p); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-connection.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-connection.hxx new file mode 100644 index 000000000..daa0808c0 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-connection.hxx @@ -0,0 +1,14 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE result_connection : callgate +{ + friend class pqxx::connection; + + result_connection(reference x) : super(x) {} + + operator bool() const { return bool(home()); } + bool operator!() const { return not home(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-creation.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-creation.hxx new file mode 100644 index 000000000..3d9205f2c --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-creation.hxx @@ -0,0 +1,24 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE result_creation : callgate +{ + friend class pqxx::connection; + friend class pqxx::pipeline; + + result_creation(reference x) : super(x) {} + + static result create( + internal::pq::PGresult *rhs, std::shared_ptr const &query, + encoding_group enc) + { + return result(rhs, query, enc); + } + + void check_status(std::string_view desc = ""sv) const + { + return home().check_status(desc); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-pipeline.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-pipeline.hxx new file mode 100644 index 000000000..3ebe436d2 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-pipeline.hxx @@ -0,0 +1,16 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE result_pipeline : callgate +{ + friend class pqxx::pipeline; + + result_pipeline(reference x) : super(x) {} + + std::shared_ptr query_ptr() const + { + return home().query_ptr(); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-sql_cursor.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-sql_cursor.hxx new file mode 100644 index 000000000..78b450739 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/result-sql_cursor.hxx @@ -0,0 +1,13 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE result_sql_cursor : callgate +{ + friend class pqxx::internal::sql_cursor; + + result_sql_cursor(reference x) : super(x) {} + + char const *cmd_status() const noexcept { return home().cmd_status(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/transaction-sql_cursor.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/transaction-sql_cursor.hxx new file mode 100644 index 000000000..4ed78dc93 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/transaction-sql_cursor.hxx @@ -0,0 +1,10 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE transaction_sql_cursor : callgate +{ + friend class pqxx::internal::sql_cursor; + transaction_sql_cursor(reference x) : super(x) {} +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/gates/transaction-transaction_focus.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/transaction-transaction_focus.hxx new file mode 100644 index 000000000..ca7939a99 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/gates/transaction-transaction_focus.hxx @@ -0,0 +1,30 @@ +#include + +#include "pqxx/transaction_base.hxx" + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE transaction_transaction_focus : callgate +{ + friend class pqxx::transaction_focus; + + transaction_transaction_focus(reference x) : super(x) {} + + void register_focus(transaction_focus *focus) + { + home().register_focus(focus); + } + void unregister_focus(transaction_focus *focus) noexcept + { + home().unregister_focus(focus); + } + void register_pending_error(zview error) + { + home().register_pending_error(error); + } + void register_pending_error(std::string &&error) + { + home().register_pending_error(std::move(error)); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/header-post.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/header-post.hxx new file mode 100644 index 000000000..ff6bf8986 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/header-post.hxx @@ -0,0 +1,22 @@ +/* Compiler deficiency workarounds for compiling libpqxx headers. + * + * To be included at the end of each libpqxx header, in order to restore the + * client program's settings. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +// NO GUARDS HERE! This code should be executed every time! + +#if defined(_MSC_VER) +# pragma warning(pop) // Restore compiler's warning state +#endif + +#if !defined(PQXX_HEADER_PRE) +# error "Include pqxx/internal/header-post.hxx AFTER its 'pre' counterpart." +#endif + +#undef PQXX_HEADER_PRE diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/header-pre.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/header-pre.hxx new file mode 100644 index 000000000..abc1a398d --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/header-pre.hxx @@ -0,0 +1,169 @@ +/* Compiler settings for compiling libpqxx headers, and workarounds for all. + * + * Include this before including any other libpqxx headers from within libpqxx. + * And to balance it out, also include header-post.hxx at the end of the batch + * of headers. + * + * The public libpqxx headers (e.g. ``) include this already; + * there's no need to do this from within an application. + * + * Include this file at the highest aggregation level possible to avoid nesting + * and to keep things simple. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ + +// NO GUARD HERE! This part should be included every time this file is. +#if defined(_MSC_VER) + +// Save compiler's warning state, and set warning level 4 for maximum +// sensitivity to warnings. +# pragma warning(push, 4) + +// Visual C++ generates some entirely unreasonable warnings. Disable them. +// Copy constructor could not be generated. +# pragma warning(disable : 4511) +// Assignment operator could not be generated. +# pragma warning(disable : 4512) +// Can't expose outside classes without exporting them. Except the MSVC docs +// say please ignore the warning if it's a standard library class. +# pragma warning(disable : 4251) +// Can't derive library classes from outside classes without exporting them. +// Except the MSVC docs say please ignore the warning if the parent class is +// in the standard library. +# pragma warning(disable : 4275) +// Can't inherit from non-exported class. +# pragma warning(disable : 4275) + +#endif // _MSC_VER + + +#if defined(PQXX_HEADER_PRE) +# error "Avoid nesting #include of pqxx/internal/header-pre.hxx." +#endif + +#define PQXX_HEADER_PRE + + +// Workarounds & definitions that need to be included even in library's headers +#include "pqxx/config-public-compiler.h" + +// Enable ISO-646 alternative operaotr representations: "and" instead of "&&" +// etc. on older compilers. C++20 removes this header. +#if __has_include() +# include +#endif + + +#if defined(PQXX_HAVE_GCC_PURE) +/// Declare function "pure": no side effects, only reads globals and its args. +# define PQXX_PURE __attribute__((pure)) +#else +# define PQXX_PURE /* pure */ +#endif + + +#if defined(__GNUC__) +/// Tell the compiler to optimise a function for size, not speed. +# define PQXX_COLD __attribute__((cold)) +#else +# define PQXX_COLD /* cold */ +#endif + + +// Workarounds for Windows +#ifdef _WIN32 + +/* For now, export DLL symbols if _DLL is defined. This is done automatically + * by the compiler when linking to the dynamic version of the runtime library, + * according to "gzh" + */ +# if defined(PQXX_SHARED) && !defined(PQXX_LIBEXPORT) +# define PQXX_LIBEXPORT __declspec(dllimport) +# endif // PQXX_SHARED && !PQXX_LIBEXPORT + + +// Workarounds for Microsoft Visual C++ +# ifdef _MSC_VER + +// Suppress vtables on abstract classes. +# define PQXX_NOVTABLE __declspec(novtable) + +// Automatically link with the appropriate libpq (static or dynamic, debug or +// release). The default is to use the release DLL. Define PQXX_PQ_STATIC to +// link to a static version of libpq, and _DEBUG to link to a debug version. +// The two may be combined. +# if defined(PQXX_AUTOLINK) +# if defined(PQXX_PQ_STATIC) +# ifdef _DEBUG +# pragma comment(lib, "libpqd") +# else +# pragma comment(lib, "libpq") +# endif +# else +# ifdef _DEBUG +# pragma comment(lib, "libpqddll") +# else +# pragma comment(lib, "libpqdll") +# endif +# endif +# endif + +// If we're not compiling libpqxx itself, automatically link with the +// appropriate libpqxx library. To link with the libpqxx DLL, define +// PQXX_SHARED; the default is to link with the static library. A static link +// is the recommended practice. +// +// The preprocessor macro PQXX_INTERNAL is used to detect whether we +// are compiling the libpqxx library itself. When you compile the library +// yourself using your own project file, make sure to include this macro. +# if defined(PQXX_AUTOLINK) && !defined(PQXX_INTERNAL) +# ifdef PQXX_SHARED +# ifdef _DEBUG +# pragma comment(lib, "libpqxxD") +# else +# pragma comment(lib, "libpqxx") +# endif +# else // !PQXX_SHARED +# ifdef _DEBUG +# pragma comment(lib, "libpqxx_staticD") +# else +# pragma comment(lib, "libpqxx_static") +# endif +# endif +# endif + +# endif // _MSC_VER + +#elif defined(PQXX_HAVE_GCC_VISIBILITY) // !_WIN32 + +# define PQXX_LIBEXPORT __attribute__((visibility("default"))) +# define PQXX_PRIVATE __attribute__((visibility("hidden"))) + +#endif // PQXX_HAVE_GCC_VISIBILITY + + +#ifndef PQXX_LIBEXPORT +# define PQXX_LIBEXPORT /* libexport */ +#endif + +#ifndef PQXX_PRIVATE +# define PQXX_PRIVATE /* private */ +#endif + +#ifndef PQXX_NOVTABLE +# define PQXX_NOVTABLE /* novtable */ +#endif + +// C++20: Assume support. +#if defined(PQXX_HAVE_LIKELY) +# define PQXX_LIKELY [[likely]] +# define PQXX_UNLIKELY [[unlikely]] +#else +# define PQXX_LIKELY /* [[likely]] */ +# define PQXX_UNLIKELY /* [[unlikely]] */ +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/ignore-deprecated-post.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/ignore-deprecated-post.hxx new file mode 100644 index 000000000..cebcf0594 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/ignore-deprecated-post.hxx @@ -0,0 +1,15 @@ +/// End a code block started by "ignore-deprecated-pre.hxx". + +#if !defined(PQXX_IGNORING_DEPRECATED) +# error "Ended an 'ignore-deprecated' block while none was active." +#endif + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif // __GNUC__ + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#undef PQXX_IGNORING_DEPRECATED diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/ignore-deprecated-pre.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/ignore-deprecated-pre.hxx new file mode 100644 index 000000000..8ac57afaa --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/ignore-deprecated-pre.hxx @@ -0,0 +1,28 @@ +/** Start a block of deprecated code which may call other deprecated code. + * + * Most compilers will emit warnings when deprecated code is invoked from + * non-deprecated code. But some compilers (notably gcc) will always emit the + * warning even when the calling code is also deprecated. + * + * This header starts a block where those warnings are suppressed. It can be + * included inside a code block. + * + * Always match the #include with a closing #include of + * "ignore-deprecated-post.hxx". To avoid mistakes, keep the enclosed area as + * small as possible. + */ +#if defined(PQXX_IGNORING_DEPRECATED) +# error "Started an 'ignore-deprecated' block inside another." +#endif + +#define PQXX_IGNORING_DEPRECATED + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif // __GNUC__ + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4996) +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/libpq-forward.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/libpq-forward.hxx new file mode 100644 index 000000000..9e74f79ec --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/libpq-forward.hxx @@ -0,0 +1,31 @@ +/** Minimal forward declarations of libpq types needed in libpqxx headers. + * + * DO NOT INCLUDE THIS FILE when building client programs. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +extern "C" +{ + struct pg_conn; + struct pg_result; + struct pgNotify; +} + +/// Forward declarations of libpq types as needed in libpqxx headers. +namespace pqxx::internal::pq +{ +using PGconn = pg_conn; +using PGresult = pg_result; +using PGnotify = pgNotify; +using PQnoticeProcessor = void (*)(void *, char const *); +} // namespace pqxx::internal::pq + +namespace pqxx +{ +/// PostgreSQL database row identifier. +using oid = unsigned int; +} // namespace pqxx diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/result_iter.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/result_iter.hxx new file mode 100644 index 000000000..1fa1f7d8a --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/result_iter.hxx @@ -0,0 +1,124 @@ +/** Result loops. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_RESULT_ITER +#define PQXX_H_RESULT_ITER + +#include + +#include "pqxx/strconv.hxx" + +namespace pqxx +{ +class result; +} // namespace pqxx + + +namespace pqxx::internal +{ +// C++20: Replace with generator? +/// Iterator for looped unpacking of a result. +template class result_iter +{ +public: + using value_type = std::tuple; + + /// Construct an "end" iterator. + result_iter() = default; + + explicit result_iter(result const &home) : + m_home{&home}, m_size{std::size(home)} + { + if (not std::empty(home)) + read(); + } + result_iter(result_iter const &) = default; + + result_iter &operator++() + { + m_index++; + if (m_index >= m_size) + m_home = nullptr; + else + read(); + return *this; + } + + /// Comparison only works for comparing to end(). + bool operator==(result_iter const &rhs) const + { + return m_home == rhs.m_home; + } + bool operator!=(result_iter const &rhs) const { return not(*this == rhs); } + + value_type const &operator*() const { return m_value; } + +private: + void read() { (*m_home)[m_index].convert(m_value); } + + result const *m_home{nullptr}; + result::size_type m_index{0}; + result::size_type m_size; + value_type m_value; +}; + + +template class result_iteration +{ +public: + using iterator = result_iter; + explicit result_iteration(result const &home) : m_home{home} + { + constexpr auto tup_size{sizeof...(TYPE)}; + if (home.columns() != tup_size) + throw usage_error{internal::concat( + "Tried to extract ", to_string(tup_size), + " field(s) from a result with ", to_string(home.columns()), + " column(s).")}; + } + iterator begin() const + { + if (std::size(m_home) == 0) + return end(); + else + return iterator{m_home}; + } + iterator end() const { return {}; } + +private: + pqxx::result const &m_home; +}; +} // namespace pqxx::internal + + +template inline auto pqxx::result::iter() const +{ + return pqxx::internal::result_iteration{*this}; +} + + +template +inline void pqxx::result::for_each(CALLABLE &&func) const +{ + using args_tuple = internal::args_t; + constexpr auto sz{std::tuple_size_v}; + static_assert( + sz > 0, + "Callback for for_each must take parameters, one for each column in the " + "result."); + + auto const cols{this->columns()}; + if (sz != cols) + throw usage_error{internal::concat( + "Callback to for_each takes ", sz, "parameter", (sz == 1) ? "" : "s", + ", but result set has ", cols, "field", (cols == 1) ? "" : "s", ".")}; + + using pass_tuple = pqxx::internal::strip_types_t; + for (auto const r : *this) std::apply(func, r.as_tuple()); +} +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/result_iterator.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/result_iterator.hxx new file mode 100644 index 000000000..3f27a1d3f --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/result_iterator.hxx @@ -0,0 +1,389 @@ +/* Definitions for the pqxx::result class and support classes. + * + * pqxx::result represents the set of result rows from a database query. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/result instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_RESULT_ITERATOR +#define PQXX_H_RESULT_ITERATOR + +#include "pqxx/row.hxx" + + +/* Result iterator. + * + * Don't include this header from your own application; it is included for you + * by other libpqxx headers. + */ + +namespace pqxx +{ +/// Iterator for rows in a result. Use as result::const_iterator. +/** A result, once obtained, cannot be modified. Therefore there is no + * plain iterator type for result. However its const_iterator type can be + * used to inspect its rows without changing them. + */ +class PQXX_LIBEXPORT const_result_iterator : public row +{ +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = row const; + using pointer = row const *; + using reference = row; + using size_type = result_size_type; + using difference_type = result_difference_type; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + /// Create an iterator, but in an unusable state. + const_result_iterator() noexcept = default; + /// Copy an iterator. + const_result_iterator(const_result_iterator const &) noexcept = default; + /// Move an iterator. + const_result_iterator(const_result_iterator &&) noexcept = default; + + /// Begin iterating a @ref row. + const_result_iterator(row const &t) noexcept : row{t} {} +#include "pqxx/internal/ignore-deprecated-post.hxx" + + /** + * @name Dereferencing operators + * + * An iterator "points to" its own row, which is also itself. This makes it + * easy to address a @ref result as a two-dimensional container, without + * going through the intermediate step of dereferencing the iterator. It + * makes the interface similar to C pointer/array semantics. + * + * IIRC Alex Stepanov, the inventor of the STL, once remarked that having + * this as standard behaviour for pointers would be useful in some + * algorithms. So even if this makes me look foolish, I would seem to be in + * distinguished company. + */ + //@{ + /// Dereference the iterator. + [[nodiscard]] pointer operator->() const { return this; } + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + /// Dereference the iterator. + [[nodiscard]] reference operator*() const { return *this; } +#include "pqxx/internal/ignore-deprecated-post.hxx" + //@} + + /** + * @name Field access + */ + //@{ + using row::back; + using row::front; + using row::operator[]; + using row::at; + using row::rownumber; + //@} + + /** + * @name Manipulations + */ + //@{ + const_result_iterator &operator=(const_result_iterator const &rhs) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + row::operator=(rhs); +#include "pqxx/internal/ignore-deprecated-post.hxx" + return *this; + } + + const_result_iterator &operator=(const_result_iterator &&rhs) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + row::operator=(std::move(rhs)); +#include "pqxx/internal/ignore-deprecated-post.hxx" + return *this; + } + + const_result_iterator operator++(int); + const_result_iterator &operator++() + { + ++m_index; + return *this; + } + const_result_iterator operator--(int); + const_result_iterator &operator--() + { + --m_index; + return *this; + } + + const_result_iterator &operator+=(difference_type i) + { + m_index += i; + return *this; + } + const_result_iterator &operator-=(difference_type i) + { + m_index -= i; + return *this; + } + + /// Interchange two iterators in an exception-safe manner. + void swap(const_result_iterator &other) noexcept + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + row::swap(other); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + //@} + + /** + * @name Comparisons + */ + //@{ + [[nodiscard]] bool operator==(const_result_iterator const &i) const + { + return m_index == i.m_index; + } + [[nodiscard]] bool operator!=(const_result_iterator const &i) const + { + return m_index != i.m_index; + } + [[nodiscard]] bool operator<(const_result_iterator const &i) const + { + return m_index < i.m_index; + } + [[nodiscard]] bool operator<=(const_result_iterator const &i) const + { + return m_index <= i.m_index; + } + [[nodiscard]] bool operator>(const_result_iterator const &i) const + { + return m_index > i.m_index; + } + [[nodiscard]] bool operator>=(const_result_iterator const &i) const + { + return m_index >= i.m_index; + } + //@} + + /** + * @name Arithmetic operators + */ + //@{ + [[nodiscard]] inline const_result_iterator operator+(difference_type) const; + friend const_result_iterator + operator+(difference_type, const_result_iterator const &); + [[nodiscard]] inline const_result_iterator operator-(difference_type) const; + [[nodiscard]] inline difference_type + operator-(const_result_iterator const &) const; + //@} + +private: + friend class pqxx::result; + const_result_iterator(pqxx::result const *r, result_size_type i) noexcept : + row{*r, i, r->columns()} + {} +}; + + +/// Reverse iterator for result. Use as result::const_reverse_iterator. +class PQXX_LIBEXPORT const_reverse_result_iterator + : private const_result_iterator +{ +public: + using super = const_result_iterator; + using iterator_type = const_result_iterator; + using iterator_type::difference_type; + using iterator_type::iterator_category; + using iterator_type::pointer; + using value_type = iterator_type::value_type; + using reference = iterator_type::reference; + + /// Create an iterator, but in an unusable state. + const_reverse_result_iterator() = default; + /// Copy an iterator. + const_reverse_result_iterator(const_reverse_result_iterator const &rhs) = + default; + /// Copy a reverse iterator from a regular iterator. + explicit const_reverse_result_iterator(const_result_iterator const &rhs) : + const_result_iterator{rhs} + { + super::operator--(); + } + + /// Move a regular iterator into a reverse iterator. + explicit const_reverse_result_iterator(const_result_iterator const &&rhs) : + const_result_iterator{std::move(rhs)} + { + super::operator--(); + } + + /// Return the underlying "regular" iterator (as per standard library). + [[nodiscard]] PQXX_PURE const_result_iterator base() const noexcept; + + /** + * @name Dereferencing operators + */ + //@{ + /// Dereference iterator. + using const_result_iterator::operator->; + /// Dereference iterator. + using const_result_iterator::operator*; + //@} + + /** + * @name Field access + */ + //@{ + using const_result_iterator::back; + using const_result_iterator::front; + using const_result_iterator::operator[]; + using const_result_iterator::at; + using const_result_iterator::rownumber; + //@} + + /** + * @name Manipulations + */ + //@{ + const_reverse_result_iterator & + operator=(const_reverse_result_iterator const &r) + { + iterator_type::operator=(r); + return *this; + } + const_reverse_result_iterator &operator=(const_reverse_result_iterator &&r) + { + iterator_type::operator=(std::move(r)); + return *this; + } + const_reverse_result_iterator &operator++() + { + iterator_type::operator--(); + return *this; + } + const_reverse_result_iterator operator++(int); + const_reverse_result_iterator &operator--() + { + iterator_type::operator++(); + return *this; + } + const_reverse_result_iterator operator--(int); + const_reverse_result_iterator &operator+=(difference_type i) + { + iterator_type::operator-=(i); + return *this; + } + const_reverse_result_iterator &operator-=(difference_type i) + { + iterator_type::operator+=(i); + return *this; + } + + void swap(const_reverse_result_iterator &other) noexcept + { + const_result_iterator::swap(other); + } + //@} + + /** + * @name Arithmetic operators + */ + //@{ + [[nodiscard]] const_reverse_result_iterator + operator+(difference_type i) const + { + return const_reverse_result_iterator(base() - i); + } + [[nodiscard]] const_reverse_result_iterator operator-(difference_type i) + { + return const_reverse_result_iterator(base() + i); + } + [[nodiscard]] difference_type + operator-(const_reverse_result_iterator const &rhs) const + { + return rhs.const_result_iterator::operator-(*this); + } + //@} + + /** + * @name Comparisons + */ + //@{ + [[nodiscard]] bool + operator==(const_reverse_result_iterator const &rhs) const noexcept + { + return iterator_type::operator==(rhs); + } + [[nodiscard]] bool + operator!=(const_reverse_result_iterator const &rhs) const noexcept + { + return not operator==(rhs); + } + + [[nodiscard]] bool operator<(const_reverse_result_iterator const &rhs) const + { + return iterator_type::operator>(rhs); + } + [[nodiscard]] bool operator<=(const_reverse_result_iterator const &rhs) const + { + return iterator_type::operator>=(rhs); + } + [[nodiscard]] bool operator>(const_reverse_result_iterator const &rhs) const + { + return iterator_type::operator<(rhs); + } + [[nodiscard]] bool operator>=(const_reverse_result_iterator const &rhs) const + { + return iterator_type::operator<=(rhs); + } + //@} +}; + + +inline const_result_iterator +const_result_iterator::operator+(result::difference_type o) const +{ + return {&m_result, size_type(result::difference_type(m_index) + o)}; +} + +inline const_result_iterator +operator+(result::difference_type o, const_result_iterator const &i) +{ + return i + o; +} + +inline const_result_iterator +const_result_iterator::operator-(result::difference_type o) const +{ + return {&m_result, result_size_type(result::difference_type(m_index) - o)}; +} + +inline result::difference_type +const_result_iterator::operator-(const const_result_iterator &i) const +{ + return result::difference_type(num() - i.num()); +} + +inline const_result_iterator result::end() const noexcept +{ + return {this, size()}; +} + + +inline const_result_iterator result::cend() const noexcept +{ + return end(); +} + + +inline const_reverse_result_iterator +operator+(result::difference_type n, const_reverse_result_iterator const &i) +{ + return const_reverse_result_iterator{i.base() - n}; +} + +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/sql_cursor.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/sql_cursor.hxx new file mode 100644 index 000000000..a26d06306 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/sql_cursor.hxx @@ -0,0 +1,118 @@ +/** Internal wrapper for SQL cursors. Supports higher-level cursor classes. + * + * DO NOT INCLUDE THIS FILE DIRECTLY. Other headers include it for you. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_SQL_CURSOR +#define PQXX_H_SQL_CURSOR + +namespace pqxx::internal +{ +/// Cursor with SQL positioning semantics. +/** Thin wrapper around an SQL cursor, with SQL's ideas of positioning. + * + * SQL cursors have pre-increment/pre-decrement semantics, with on either end + * of the result set a special position that does not repesent a row. This + * class models SQL cursors for the purpose of implementing more C++-like + * semantics on top. + * + * Positions of actual rows are numbered starting at 1. Position 0 exists but + * does not refer to a row. There is a similar non-row position at the end of + * the result set. + * + * Don't use this at home. You deserve better. Use the stateles_cursor + * instead. + */ +class PQXX_LIBEXPORT sql_cursor : public cursor_base +{ +public: + sql_cursor( + transaction_base &t, std::string_view query, std::string_view cname, + cursor_base::access_policy ap, cursor_base::update_policy up, + cursor_base::ownership_policy op, bool hold); + + sql_cursor( + transaction_base &t, std::string_view cname, + cursor_base::ownership_policy op); + + ~sql_cursor() noexcept { close(); } + + result fetch(difference_type rows, difference_type &displacement); + result fetch(difference_type rows) + { + difference_type d = 0; + return fetch(rows, d); + } + difference_type move(difference_type rows, difference_type &displacement); + difference_type move(difference_type rows) + { + difference_type d = 0; + return move(rows, d); + } + + /// Current position, or -1 for unknown + /** + * The starting position, just before the first row, counts as position zero. + * + * Position may be unknown if (and only if) this cursor was adopted, and has + * never hit its starting position (position zero). + */ + difference_type pos() const noexcept { return m_pos; } + + /// End position, or -1 for unknown + /** + * Returns the final position, just after the last row in the result set. The + * starting position, just before the first row, counts as position zero. + * + * End position is unknown until it is encountered during use. + */ + difference_type endpos() const noexcept { return m_endpos; } + + /// Return zero-row result for this cursor. + result const &empty_result() const noexcept { return m_empty_result; } + + void close() noexcept; + +private: + difference_type adjust(difference_type hoped, difference_type actual); + static std::string stridestring(difference_type); + /// Initialize cached empty result. Call only at beginning or end! + void init_empty_result(transaction_base &); + + /// Connection in which this cursor lives. + connection &m_home; + + /// Zero-row result from this cursor (or plain empty one if cursor is + /// adopted) + result m_empty_result; + + result m_cached_current_row; + + /// Is this cursor adopted (as opposed to created by this cursor object)? + bool m_adopted; + + /// Will this cursor object destroy its SQL cursor when it dies? + cursor_base::ownership_policy m_ownership; + + /// At starting position (-1), somewhere in the middle (0), or past end (1) + int m_at_end; + + /// Position, or -1 for unknown + difference_type m_pos; + + /// End position, or -1 for unknown + difference_type m_endpos = -1; +}; + + +PQXX_LIBEXPORT result_size_type obtain_stateless_cursor_size(sql_cursor &); +PQXX_LIBEXPORT result stateless_cursor_retrieve( + sql_cursor &, result::difference_type size, + result::difference_type begin_pos, result::difference_type end_pos); +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/statement_parameters.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/statement_parameters.hxx new file mode 100644 index 000000000..b078bf6e0 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/statement_parameters.hxx @@ -0,0 +1,131 @@ +/** Common implementation for statement parameter lists. + * + * These are used for both prepared statements and parameterized statements. + * + * DO NOT INCLUDE THIS FILE DIRECTLY. Other headers include it for you. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_STATEMENT_PARAMETER +#define PQXX_H_STATEMENT_PARAMETER + +#include +#include +#include +#include +#include + +#include "pqxx/binarystring.hxx" +#include "pqxx/strconv.hxx" +#include "pqxx/util.hxx" + + +namespace pqxx::internal +{ +template +constexpr inline auto const iterator_identity{ + [](decltype(*std::declval()) x) { return x; }}; + + +/// Marker type: pass a dynamically-determined number of statement parameters. +/** @deprecated Use @ref params instead. + * + * Normally when invoking a prepared or parameterised statement, the number + * of parameters is known at compile time. For instance, + * `t.exec_prepared("foo", 1, "x");` executes statement `foo` with two + * parameters, an `int` and a C string. + * + * But sometimes you may want to pass a number of parameters known only at run + * time. In those cases, a @ref dynamic_params encodes a dynamically + * determined number of parameters. You can mix these with regular, static + * parameter lists, and you can re-use them for multiple statement invocations. + * + * A dynamic_params object does not store copies of its parameters, so make + * sure they remain accessible until you've executed the statement. + * + * The ACCESSOR is an optional callable (such as a lambda). If you pass an + * accessor `a`, then each parameter `p` goes into your statement as `a(p)`. + */ +template)> +class dynamic_params +{ +public: + /// Wrap a sequence of pointers or iterators. + constexpr dynamic_params(IT begin, IT end) : + m_begin(begin), m_end(end), m_accessor(iterator_identity) + {} + + /// Wrap a sequence of pointers or iterators. + /** This version takes an accessor callable. If you pass an accessor `acc`, + * then any parameter `p` will go into the statement's parameter list as + * `acc(p)`. + */ + constexpr dynamic_params(IT begin, IT end, ACCESSOR &acc) : + m_begin(begin), m_end(end), m_accessor(acc) + {} + + /// Wrap a container. + template + explicit constexpr dynamic_params(C &container) : + dynamic_params(std::begin(container), std::end(container)) + {} + + /// Wrap a container. + /** This version takes an accessor callable. If you pass an accessor `acc`, + * then any parameter `p` will go into the statement's parameter list as + * `acc(p)`. + */ + template + explicit constexpr dynamic_params(C &container, ACCESSOR &acc) : + dynamic_params(std::begin(container), std::end(container), acc) + {} + + constexpr IT begin() const noexcept { return m_begin; } + constexpr IT end() const noexcept { return m_end; } + + constexpr auto access(decltype(*std::declval()) value) const + -> decltype(std::declval()(value)) + { + return m_accessor(value); + } + +private: + IT const m_begin, m_end; + ACCESSOR m_accessor = iterator_identity; +}; + + +/// Internal type: encode statement parameters. +/** Compiles arguments for prepared statements and parameterised queries into + * a format that can be passed into libpq. + * + * Objects of this type are meant to be short-lived: a `c_params` lives and + * dies entirely within the call to execute. So, for example, if you pass in a + * non-null pointer as a parameter, @ref params may simply use that pointer as + * a parameter value, without arranging longer-term storage for the data to + * which it points. All values referenced by parameters must remain "live" + * until the parameterised or prepared statement has been executed. + */ +struct PQXX_LIBEXPORT c_params +{ + c_params() = default; + /// Copying these objects is pointless and expensive. Don't do it. + c_params(c_params const &) = delete; + c_params(c_params &&) = default; + + /// Pre-allocate storage for `n` parameters. + void reserve(std::size_t n) &; + + /// As used by libpq: pointers to parameter values. + std::vector values; + /// As used by libpq: lengths of non-null arguments, in bytes. + std::vector lengths; + /// As used by libpq: effectively boolean "is this a binary parameter?" + std::vector formats; +}; +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/stream_iterator.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/stream_iterator.hxx new file mode 100644 index 000000000..f240dcfa7 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/stream_iterator.hxx @@ -0,0 +1,105 @@ +/** Stream iterators. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_STREAM_ITERATOR +#define PQXX_H_STREAM_ITERATOR + +#include + +namespace pqxx +{ +class stream_from; +} + + +namespace pqxx::internal +{ +// C++20: Replace with generator? +/// Input iterator for stream_from. +/** Just barely enough to support range-based "for" loops. Don't assume that + * any of the usual behaviour works beyond that. + */ +template class stream_input_iterator +{ +public: + using value_type = std::tuple; + + /// Construct an "end" iterator. + stream_input_iterator() = default; + + explicit stream_input_iterator(stream_from &home) : m_home(&home) + { + advance(); + } + stream_input_iterator(stream_input_iterator const &) = default; + + stream_input_iterator &operator++() + { + advance(); + return *this; + } + + value_type const &operator*() const { return m_value; } + + /// Comparison only works for comparing to end(). + bool operator==(stream_input_iterator const &rhs) const + { + return m_home == rhs.m_home; + } + /// Comparison only works for comparing to end(). + bool operator!=(stream_input_iterator const &rhs) const + { + return not(*this == rhs); + } + +private: + void advance() + { + if (m_home == nullptr) + throw usage_error{"Moving stream_from iterator beyond end()."}; + if (not((*m_home) >> m_value)) + m_home = nullptr; + } + + stream_from *m_home{nullptr}; + value_type m_value; +}; + + +// C++20: Replace with generator? +/// Iteration over a @ref stream_from. +template class stream_input_iteration +{ +public: + using iterator = stream_input_iterator; + explicit stream_input_iteration(stream_from &home) : m_home{home} {} + iterator begin() const { return iterator{m_home}; } + iterator end() const { return {}; } + +private: + stream_from &m_home; +}; + + +// C++20: Replace with generator? +/// Iteration over a @ref stream_from, deleting it once done. +template class owning_stream_input_iteration +{ +public: + using iterator = stream_input_iterator; + explicit owning_stream_input_iteration(std::unique_ptr &&home) : + m_home{std::move(home)} + {} + iterator begin() const { return iterator{*m_home.get()}; } + iterator end() const { return {}; } + +private: + std::unique_ptr m_home; +}; +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/internal/wait.hxx b/ext/libpqxx-7.7.3/include/pqxx/internal/wait.hxx new file mode 100644 index 000000000..7a82e6553 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/internal/wait.hxx @@ -0,0 +1,18 @@ +#if !defined(PQXX_WAIT_HXX) +# define PQXX_WAIT_HXX + +namespace pqxx::internal +{ +/// Wait. +/** This is normally `std::this_thread::sleep_for()`. But MinGW's `thread` + * header doesn't work, so we must be careful about including it. + */ +void PQXX_LIBEXPORT wait_for(unsigned int microseconds); + + +/// Wait for a socket to be ready for reading/writing, or timeout. +PQXX_LIBEXPORT void wait_fd( + int fd, bool for_read, bool for_write, unsigned seconds = 1, + unsigned microseconds = 0); +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/isolation b/ext/libpqxx-7.7.3/include/pqxx/isolation new file mode 100644 index 000000000..1b801329b --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/isolation @@ -0,0 +1,8 @@ +/** Transaction isolation levels. + * + * Policies and traits describing SQL transaction isolation levels + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/isolation.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/isolation.hxx b/ext/libpqxx-7.7.3/include/pqxx/isolation.hxx new file mode 100644 index 000000000..0698c6ab4 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/isolation.hxx @@ -0,0 +1,75 @@ +/* Definitions for transaction isolation levels, and such. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/isolation instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ISOLATION +#define PQXX_H_ISOLATION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/util.hxx" + +namespace pqxx +{ +/// Should a transaction be read-only, or read-write? +/** No, this is not an isolation level. So it really doesn't belong here. + * But it's not really worth a separate header. + */ +enum class write_policy +{ + read_only, + read_write +}; + + +/// Transaction isolation levels. +/** These are as defined in the SQL standard. But there are a few notes + * specific to PostgreSQL. + * + * First, postgres does not support "read uncommitted." The lowest level you + * can get is "read committed," which is better. PostgreSQL is built on the + * MVCC paradigm, which guarantees "read committed" isolation without any + * additional performance overhead, so there was no point in providing the + * lower level. + * + * Second, "repeatable read" also makes more isolation guarantees than the + * standard requires. According to the standard, this level prevents "dirty + * reads" and "nonrepeatable reads," but not "phantom reads." In postgres, + * it actually prevents all three. + * + * Third, "serializable" is only properly supported starting at postgres 9.1. + * If you request "serializable" isolation on an older backend, you will get + * the same isolation as in "repeatable read." It's better than the + * "repeatable read" defined in the SQL standard, but not a complete + * implementation of the standard's "serializable" isolation level. + * + * In general, a lower isolation level will allow more surprising interactions + * between ongoing transactions, but improve performance. A higher level + * gives you more protection from subtle concurrency bugs, but sometimes it + * may not be possible to complete your transaction without avoiding paradoxes + * in the data. In that case a transaction may fail, and the application will + * have to re-do the whole thing based on the latest state of the database. + * (If you want to retry your code in that situation, have a look at the + * transactor framework.) + * + * Study the levels and design your application with the right level in mind. + */ +enum isolation_level +{ + // PostgreSQL only has the better isolation levels. + // read_uncommitted, + + read_committed, + repeatable_read, + serializable, +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/largeobject b/ext/libpqxx-7.7.3/include/pqxx/largeobject new file mode 100644 index 000000000..1f2f94790 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/largeobject @@ -0,0 +1,8 @@ +/** Large Objects interface. + * + * Supports direct access to large objects, as well as through I/O streams + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/largeobject.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/largeobject.hxx b/ext/libpqxx-7.7.3/include/pqxx/largeobject.hxx new file mode 100644 index 000000000..ebafc51d8 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/largeobject.hxx @@ -0,0 +1,735 @@ +/* Large Objects interface. Deprecated; use blob instead. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/largeobject instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_LARGEOBJECT +#define PQXX_H_LARGEOBJECT + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#include "pqxx/dbtransaction.hxx" + + +namespace pqxx +{ +/// Identity of a large object. +/** @deprecated Use the @ref blob class instead. + * + * Encapsulates the identity of a large object. + * + * A largeobject must be accessed only from within a backend transaction, but + * the object's identity remains valid as long as the object exists. + */ +class PQXX_LIBEXPORT largeobject +{ +public: + using size_type = large_object_size_type; + + /// Refer to a nonexistent large object (similar to what a null pointer + /// does). + [[deprecated("Use blob instead.")]] largeobject() noexcept = default; + + /// Create new large object. + /** @param t Backend transaction in which the object is to be created. + */ + [[deprecated("Use blob instead.")]] explicit largeobject(dbtransaction &t); + + /// Wrap object with given oid. + /** Convert combination of a transaction and object identifier into a + * large object identity. Does not affect the database. + * @param o Object identifier for the given object. + */ + [[deprecated("Use blob instead.")]] explicit largeobject(oid o) noexcept : + m_id{o} + {} + + /// Import large object from a local file. + /** Creates a large object containing the data found in the given file. + * @param t Backend transaction in which the large object is to be created. + * @param file A filename on the client program's filesystem. + */ + [[deprecated("Use blob instead.")]] largeobject( + dbtransaction &t, std::string_view file); + + /// Take identity of an opened large object. + /** Copy identity of already opened large object. Note that this may be done + * as an implicit conversion. + * @param o Already opened large object to copy identity from. + */ + [[deprecated("Use blob instead.")]] largeobject( + largeobjectaccess const &o) noexcept; + + /// Object identifier. + /** The number returned by this function identifies the large object in the + * database we're connected to (or oid_none is returned if we refer to the + * null object). + */ + [[nodiscard]] oid id() const noexcept { return m_id; } + + /** + * @name Identity comparisons + * + * These operators compare the object identifiers of large objects. This has + * nothing to do with the objects' actual contents; use them only for keeping + * track of containers of references to large objects and such. + */ + //@{ + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator==(largeobject const &other) const + { + return m_id == other.m_id; + } + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator!=(largeobject const &other) const + { + return m_id != other.m_id; + } + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator<=(largeobject const &other) const + { + return m_id <= other.m_id; + } + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator>=(largeobject const &other) const + { + return m_id >= other.m_id; + } + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator<(largeobject const &other) const + { + return m_id < other.m_id; + } + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator>(largeobject const &other) const + { + return m_id > other.m_id; + } + //@} + + /// Export large object's contents to a local file + /** Writes the data stored in the large object to the given file. + * @param t Transaction in which the object is to be accessed + * @param file A filename on the client's filesystem + */ + void to_file(dbtransaction &t, std::string_view file) const; + + /// Delete large object from database + /** Unlike its low-level equivalent cunlink, this will throw an exception if + * deletion fails. + * @param t Transaction in which the object is to be deleted + */ + void remove(dbtransaction &t) const; + +protected: + PQXX_PURE static internal::pq::PGconn * + raw_connection(dbtransaction const &T); + + PQXX_PRIVATE std::string reason(connection const &, int err) const; + +private: + oid m_id = oid_none; +}; + + +/// Accessor for large object's contents. +/** @deprecated Use the `blob` class instead. + */ +class PQXX_LIBEXPORT largeobjectaccess : private largeobject +{ +public: + using largeobject::size_type; + using off_type = size_type; + using pos_type = size_type; + + /// Open mode: `in`, `out` (can be combined using "bitwise or"). + /** According to the C++ standard, these should be in `std::ios_base`. We + * take them from derived class `std::ios` instead, which is easier on the + * eyes. + * + * Historical note: taking it from std::ios was originally a workaround for a + * problem with gcc 2.95. + */ + using openmode = std::ios::openmode; + + /// Default open mode: in, out, binary. + static constexpr auto default_mode{ + std::ios::in | std::ios::out | std::ios::binary}; + + /// Seek direction: `beg`, `cur`, `end`. + using seekdir = std::ios::seekdir; + + /// Create new large object and open it. + /** + * @param t Backend transaction in which the object is to be created. + * @param mode Access mode, defaults to ios_base::in | ios_base::out | + * ios_base::binary. + */ + [[deprecated("Use blob instead.")]] explicit largeobjectaccess( + dbtransaction &t, openmode mode = default_mode); + + /// Open large object with given oid. + /** Convert combination of a transaction and object identifier into a + * large object identity. Does not affect the database. + * @param t Transaction in which the object is to be accessed. + * @param o Object identifier for the given object. + * @param mode Access mode, defaults to ios_base::in | ios_base::out | + * ios_base::binary. + */ + [[deprecated("Use blob instead.")]] largeobjectaccess( + dbtransaction &t, oid o, openmode mode = default_mode); + + /// Open given large object. + /** Open a large object with the given identity for reading and/or writing. + * @param t Transaction in which the object is to be accessed. + * @param o Identity for the large object to be accessed. + * @param mode Access mode, defaults to ios_base::in | ios_base::out | + * ios_base::binary. + */ + [[deprecated("Use blob instead.")]] largeobjectaccess( + dbtransaction &t, largeobject o, openmode mode = default_mode); + + /// Import large object from a local file and open it. + /** Creates a large object containing the data found in the given file. + * @param t Backend transaction in which the large object is to be created. + * @param file A filename on the client program's filesystem. + * @param mode Access mode, defaults to ios_base::in | ios_base::out. + */ + [[deprecated("Use blob instead.")]] largeobjectaccess( + dbtransaction &t, std::string_view file, openmode mode = default_mode); + + ~largeobjectaccess() noexcept { close(); } + + /// Object identifier. + /** The number returned by this function uniquely identifies the large object + * in the context of the database we're connected to. + */ + using largeobject::id; + + /// Export large object's contents to a local file. + /** Writes the data stored in the large object to the given file. + * @param file A filename on the client's filesystem. + */ + void to_file(std::string_view file) const + { + largeobject::to_file(m_trans, file); + } + + using largeobject::to_file; + + /** + * @name High-level access to object contents. + */ + //@{ + /// Write data to large object. + /** @warning The size of a write is currently limited to 2GB. + * + * @param buf Data to write. + * @param len Number of bytes from Buf to write. + */ + void write(char const buf[], std::size_t len); + + /// Write string to large object. + /** If not all bytes could be written, an exception is thrown. + * @param buf Data to write; no terminating zero is written. + */ + void write(std::string_view buf) { write(std::data(buf), std::size(buf)); } + + /// Read data from large object. + /** Throws an exception if an error occurs while reading. + * @param buf Location to store the read data in. + * @param len Number of bytes to try and read. + * @return Number of bytes read, which may be less than the number requested + * if the end of the large object is reached. + */ + size_type read(char buf[], std::size_t len); + + /// Seek in large object's data stream. + /** Throws an exception if an error occurs. + * @return The new position in the large object + */ + size_type seek(size_type dest, seekdir dir); + + /// Report current position in large object's data stream. + /** Throws an exception if an error occurs. + * @return The current position in the large object. + */ + [[nodiscard]] size_type tell() const; + //@} + + /** + * @name Low-level access to object contents. + * + * These functions provide a more "C-like" access interface, returning + * special values instead of throwing exceptions on error. These functions + * are generally best avoided in favour of the high-level access functions, + * which behave more like C++ functions should. + * + * Due to libpq's underlying API, some operations are limited to "int" + * sizes, typically 2 GB, even though a large object can grow much larger. + */ + //@{ + /// Seek in large object's data stream. + /** Does not throw exception in case of error; inspect return value and + * `errno` instead. + * @param dest Offset to go to. + * @param dir Origin to which dest is relative: ios_base::beg (from beginning + * of the object), ios_base::cur (from current access position), or + * ios_base;:end (from end of object). + * @return New position in large object, or -1 if an error occurred. + */ + pos_type cseek(off_type dest, seekdir dir) noexcept; + + /// Write to large object's data stream. + /** Does not throw exception in case of error; inspect return value and + * `errno` instead. + * @param buf Data to write. + * @param len Number of bytes to write. + * @return Number of bytes actually written, or -1 if an error occurred. + */ + off_type cwrite(char const buf[], std::size_t len) noexcept; + + /// Read from large object's data stream. + /** Does not throw exception in case of error; inspect return value and + * `errno` instead. + * @param buf Area where incoming bytes should be stored. + * @param len Number of bytes to read. + * @return Number of bytes actually read, or -1 if an error occurred.. + */ + off_type cread(char buf[], std::size_t len) noexcept; + + /// Report current position in large object's data stream. + /** Does not throw exception in case of error; inspect return value and + * `errno` instead. + * @return Current position in large object, of -1 if an error occurred. + */ + [[nodiscard]] pos_type ctell() const noexcept; + //@} + + /** + * @name Error/warning output + */ + //@{ + /// Issue message to transaction's notice processor. + void process_notice(zview) noexcept; + //@} + + using largeobject::remove; + + using largeobject::operator==; + using largeobject::operator!=; + using largeobject::operator<; + using largeobject::operator<=; + using largeobject::operator>; + using largeobject::operator>=; + + largeobjectaccess() = delete; + largeobjectaccess(largeobjectaccess const &) = delete; + largeobjectaccess operator=(largeobjectaccess const &) = delete; + +private: + PQXX_PRIVATE std::string reason(int err) const; + internal::pq::PGconn *raw_connection() const + { + return largeobject::raw_connection(m_trans); + } + + PQXX_PRIVATE void open(openmode mode); + void close() noexcept; + + dbtransaction &m_trans; + int m_fd = -1; +}; + + +/// Streambuf to use large objects in standard I/O streams. +/** @deprecated Access large objects directly using the @ref blob class. + * + * The standard streambuf classes provide uniform access to data storage such + * as files or string buffers, so they can be accessed using standard input or + * output streams. This streambuf implementation provided similar access to + * large objects, so they could be read and written using the same stream + * classes. + * + * This functionality was considered too fragile and complex, so it has been + * replaced with a single, much simpler class. + */ +template> +class largeobject_streambuf : public std::basic_streambuf +{ + using size_type = largeobject::size_type; + +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + using openmode = largeobjectaccess::openmode; + using seekdir = largeobjectaccess::seekdir; + + /// Default open mode: in, out, binary. + static constexpr auto default_mode{ + std::ios::in | std::ios::out | std::ios::binary}; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + [[deprecated("Use blob instead.")]] largeobject_streambuf( + dbtransaction &t, largeobject o, openmode mode = default_mode, + size_type buf_size = 512) : + m_bufsize{buf_size}, m_obj{t, o, mode}, m_g{nullptr}, m_p{nullptr} + { + initialize(mode); + } +#include "pqxx/internal/ignore-deprecated-post.hxx" + + [[deprecated("Use blob instead.")]] largeobject_streambuf( + dbtransaction &t, oid o, openmode mode = default_mode, + size_type buf_size = 512) : + m_bufsize{buf_size}, m_obj{t, o, mode}, m_g{nullptr}, m_p{nullptr} + { + initialize(mode); + } + + virtual ~largeobject_streambuf() noexcept + { + delete[] m_p; + delete[] m_g; + } + + /// For use by large object stream classes. + void process_notice(zview const &s) { m_obj.process_notice(s); } + +protected: + virtual int sync() override + { + // setg() sets eback, gptr, egptr. + this->setg(this->eback(), this->eback(), this->egptr()); + return overflow(eof()); + } + + virtual pos_type seekoff(off_type offset, seekdir dir, openmode) override + { + return adjust_eof(m_obj.cseek(largeobjectaccess::off_type(offset), dir)); + } + + virtual pos_type seekpos(pos_type pos, openmode) override + { + largeobjectaccess::pos_type const newpos{ + m_obj.cseek(largeobjectaccess::off_type(pos), std::ios::beg)}; + return adjust_eof(newpos); + } + + virtual int_type overflow(int_type ch) override + { + auto *const pp{this->pptr()}; + if (pp == nullptr) + return eof(); + auto *const pb{this->pbase()}; + int_type res{0}; + + if (pp > pb) + { + auto const write_sz{pp - pb}; + auto const written_sz{ + m_obj.cwrite(pb, static_cast(pp - pb))}; + if (internal::cmp_less_equal(written_sz, 0)) + throw internal_error{ + "pqxx::largeobject: write failed " + "(is transaction still valid on write or flush?), " + "libpq reports error"}; + else if (write_sz != written_sz) + throw internal_error{ + "pqxx::largeobject: write failed " + "(is transaction still valid on write or flush?), " + + std::to_string(written_sz) + "/" + std::to_string(write_sz) + + " bytes written"}; + auto const out{adjust_eof(written_sz)}; + + if constexpr (std::is_arithmetic_v) + res = check_cast(out, "largeobject position"sv); + else + res = int_type(out); + } + this->setp(m_p, m_p + m_bufsize); + + // Write that one more character, if it's there. + if (ch != eof()) + { + *this->pptr() = static_cast(ch); + this->pbump(1); + } + return res; + } + + virtual int_type overflow() { return overflow(eof()); } + + virtual int_type underflow() override + { + if (this->gptr() == nullptr) + return eof(); + auto *const eb{this->eback()}; + auto const res{adjust_eof( + m_obj.cread(this->eback(), static_cast(m_bufsize)))}; + this->setg( + eb, eb, eb + (res == eof() ? 0 : static_cast(res))); + return (res == eof() or res == 0) ? eof() : traits_type::to_int_type(*eb); + } + +private: + /// Shortcut for traits_type::eof(). + static int_type eof() { return traits_type::eof(); } + + /// Helper: change error position of -1 to EOF (probably a no-op). + template static std::streampos adjust_eof(INTYPE pos) + { + bool const at_eof{pos == -1}; + if constexpr (std::is_arithmetic_v) + { + return check_cast( + (at_eof ? eof() : pos), "large object seek"sv); + } + else + { + return std::streampos(at_eof ? eof() : pos); + } + } + + void initialize(openmode mode) + { + if ((mode & std::ios::in) != 0) + { + m_g = new char_type[unsigned(m_bufsize)]; + this->setg(m_g, m_g, m_g); + } + if ((mode & std::ios::out) != 0) + { + m_p = new char_type[unsigned(m_bufsize)]; + this->setp(m_p, m_p + m_bufsize); + } + } + + size_type const m_bufsize; + largeobjectaccess m_obj; + + /// Get & put buffers. + char_type *m_g, *m_p; +}; + + +/// Input stream that gets its data from a large object. +/** @deprecated Access large objects directly using the @ref blob class. + * + * This class worked like any other istream, but to read data from a large + * object. It supported all formatting and streaming operations of + * `std::istream`. + * + * This functionality was considered too fragile and complex, so it has been + * replaced with a single, much simpler class. + */ +template> +class basic_ilostream : public std::basic_istream +{ + using super = std::basic_istream; + +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + /// Create a basic_ilostream. + /** + * @param t Transaction in which this stream is to exist. + * @param o Large object to access. + * @param buf_size Size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_ilostream( + dbtransaction &t, largeobject o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{t, o, std::ios::in | std::ios::binary, buf_size} + { + super::init(&m_buf); + } +#include "pqxx/internal/ignore-deprecated-post.hxx" + + /// Create a basic_ilostream. + /** + * @param t Transaction in which this stream is to exist. + * @param o Identifier of a large object to access. + * @param buf_size Size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_ilostream( + dbtransaction &t, oid o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{t, o, std::ios::in | std::ios::binary, buf_size} + { + super::init(&m_buf); + } + +private: + largeobject_streambuf m_buf; +}; + +using ilostream = basic_ilostream; + + +/// Output stream that writes data back to a large object. +/** @deprecated Access large objects directly using the @ref blob class. + * + * This worked like any other ostream, but to write data to a large object. + * It supported all formatting and streaming operations of `std::ostream`. + * + * This functionality was considered too fragile and complex, so it has been + * replaced with a single, much simpler class. + */ +template> +class basic_olostream : public std::basic_ostream +{ + using super = std::basic_ostream; + +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + /// Create a basic_olostream. + /** + * @param t transaction in which this stream is to exist. + * @param o a large object to access. + * @param buf_size size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_olostream( + dbtransaction &t, largeobject o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{t, o, std::ios::out | std::ios::binary, buf_size} + { + super::init(&m_buf); + } +#include "pqxx/internal/ignore-deprecated-post.hxx" + + /// Create a basic_olostream. + /** + * @param t transaction in which this stream is to exist. + * @param o a large object to access. + * @param buf_size size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_olostream( + dbtransaction &t, oid o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{t, o, std::ios::out | std::ios::binary, buf_size} + { + super::init(&m_buf); + } + + ~basic_olostream() + { + try + { + m_buf.pubsync(); + m_buf.pubsync(); + } + catch (std::exception const &e) + { + m_buf.process_notice(e.what()); + } + } + +private: + largeobject_streambuf m_buf; +}; + +using olostream = basic_olostream; + + +/// Stream that reads and writes a large object. +/** @deprecated Access large objects directly using the @ref blob class. + * + * This worked like a std::iostream, but to read data from, or write data to, a + * large object. It supported all formatting and streaming operations of + * `std::iostream`. + * + * This functionality was considered too fragile and complex, so it has been + * replaced with a single, much simpler class. + */ +template> +class basic_lostream : public std::basic_iostream +{ + using super = std::basic_iostream; + +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + + /// Create a basic_lostream. + /** + * @param t Transaction in which this stream is to exist. + * @param o Large object to access. + * @param buf_size Size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_lostream( + dbtransaction &t, largeobject o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{ + t, o, std::ios::in | std::ios::out | std::ios::binary, buf_size} + { + super::init(&m_buf); + } + + /// Create a basic_lostream. + /** + * @param t Transaction in which this stream is to exist. + * @param o Large object to access. + * @param buf_size Size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_lostream( + dbtransaction &t, oid o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{ + t, o, std::ios::in | std::ios::out | std::ios::binary, buf_size} + { + super::init(&m_buf); + } + + ~basic_lostream() + { + try + { + m_buf.pubsync(); + m_buf.pubsync(); + } + catch (std::exception const &e) + { + m_buf.process_notice(e.what()); + } + } + +private: + largeobject_streambuf m_buf; +}; + +using lostream = basic_lostream; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/nontransaction b/ext/libpqxx-7.7.3/include/pqxx/nontransaction new file mode 100644 index 000000000..bb5b79724 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/nontransaction @@ -0,0 +1,8 @@ +/** pqxx::nontransaction class. + * + * pqxx::nontransaction provides nontransactional database access. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/nontransaction.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/nontransaction.hxx b/ext/libpqxx-7.7.3/include/pqxx/nontransaction.hxx new file mode 100644 index 000000000..c50715594 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/nontransaction.hxx @@ -0,0 +1,76 @@ +/* Definition of the pqxx::nontransaction class. + * + * pqxx::nontransaction provides nontransactional database access + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/nontransaction instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_NONTRANSACTION +#define PQXX_H_NONTRANSACTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/connection.hxx" +#include "pqxx/result.hxx" +#include "pqxx/transaction.hxx" + +namespace pqxx +{ +using namespace std::literals; + +/// Simple "transaction" class offering no transactional integrity. +/** + * @ingroup transactions + * + * nontransaction, like transaction or any other transaction_base-derived + * class, provides access to a database through a connection. Unlike its + * siblings, however, nontransaction does not maintain any kind of + * transactional integrity. This may be useful eg. for read-only access to the + * database that does not require a consistent, atomic view on its data; or for + * operations that are not allowed within a backend transaction, such as + * creating tables. + * + * For queries that update the database, however, a real transaction is likely + * to be faster unless the transaction consists of only a single record update. + * + * Also, you can keep a nontransaction open for as long as you like. Actual + * back-end transactions are limited in lifespan, and will sometimes fail just + * because they took too long to execute or were left idle for too long. This + * will not happen with a nontransaction (although the connection may still + * time out, e.g. when the network is unavailable for a very long time). + * + * Any query executed in a nontransaction is committed immediately, and neither + * commit() nor abort() has any effect. + * + * Database features that require a backend transaction, such as cursors or + * large objects, will not work in a nontransaction. + */ +class PQXX_LIBEXPORT nontransaction final : public transaction_base +{ +public: + /// Constructor. + /** Create a "dummy" transaction. + * @param c Connection in which this "transaction" will operate. + * @param tname Optional tname for the transaction, beginning with a letter + * and containing only letters and digits. + */ + nontransaction(connection &c, std::string_view tname = ""sv) : + transaction_base{c, tname, std::shared_ptr{}} + { + register_transaction(); + } + + virtual ~nontransaction() override { close(); } + +private: + virtual void do_commit() override {} +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/notification b/ext/libpqxx-7.7.3/include/pqxx/notification new file mode 100644 index 000000000..a0bd1c73e --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/notification @@ -0,0 +1,8 @@ +/** pqxx::notification_receiver functor interface. + * + * pqxx::notification_receiver handles incoming notifications. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/notification.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/notification.hxx b/ext/libpqxx-7.7.3/include/pqxx/notification.hxx new file mode 100644 index 000000000..b59b8567a --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/notification.hxx @@ -0,0 +1,94 @@ +/* Definition of the pqxx::notification_receiver functor interface. + * + * pqxx::notification_receiver handles incoming notifications. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/notification instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_NOTIFICATION +#define PQXX_H_NOTIFICATION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#include "pqxx/types.hxx" + + +namespace pqxx +{ +/// "Observer" base class for notifications. +/** @addtogroup notification Notifications and Receivers + * + * To listen on a notification issued using the NOTIFY command, derive your own + * class from notification_receiver and define its function-call operator to + * perform whatever action you wish to take when the given notification + * arrives. Then create an object of that class and pass it to your connection. + * DO NOT use raw SQL to listen for notifications, or your attempts to listen + * won't be resumed when a connection fails--and you'll have no way to notice. + * + * Notifications never arrive inside a transaction, not even in a + * nontransaction. Therefore, you are free to open a transaction of your own + * inside your receiver's function invocation operator. + * + * Notifications you are listening for may arrive anywhere within libpqxx code, + * but be aware that **PostgreSQL defers notifications occurring inside + * transactions.** (This was done for excellent reasons; just think about what + * happens if the transaction where you happen to handle an incoming + * notification is later rolled back for other reasons). So if you're keeping + * a transaction open, don't expect any of your receivers on the same + * connection to be notified. + * + * (For very similar reasons, outgoing notifications are also not sent until + * the transaction that sends them commits.) + * + * Multiple receivers on the same connection may listen on a notification of + * the same name. An incoming notification is processed by invoking all + * receivers (zero or more) of the same name. + */ +class PQXX_LIBEXPORT PQXX_NOVTABLE notification_receiver +{ +public: + /// Register the receiver with a connection. + /** + * @param c Connnection to operate on. + * @param channel Name of the notification to listen for. + */ + notification_receiver(connection &c, std::string_view channel); + /// Register the receiver with a connection. + notification_receiver(notification_receiver const &) = delete; + /// Register the receiver with a connection. + notification_receiver &operator=(notification_receiver const &) = delete; + /// Deregister the receiver. + virtual ~notification_receiver(); + + /// The channel that this receiver listens on. + [[nodiscard]] std::string const &channel() const & { return m_channel; } + + // TODO: Change API to take payload as zview instead of string ref. + /// Overridable: action to invoke when notification arrives. + /** + * @param payload An optional string that may have been passed to the NOTIFY + * command. + * @param backend_pid Process ID of the database backend process that served + * our connection when the notification arrived. The actual process ID + * behind the connection may have changed by the time this method is called. + */ + virtual void operator()(std::string const &payload, int backend_pid) = 0; + +protected: + connection &conn() const noexcept { return m_conn; } + +private: + connection &m_conn; + std::string m_channel; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/params b/ext/libpqxx-7.7.3/include/pqxx/params new file mode 100644 index 000000000..4098782aa --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/params @@ -0,0 +1,8 @@ +/** Helper classes for passing statement parameters. + * + * Use these for prepared statements and parameterised statements. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/params.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/params.hxx b/ext/libpqxx-7.7.3/include/pqxx/params.hxx new file mode 100644 index 000000000..2d29cdfed --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/params.hxx @@ -0,0 +1,383 @@ +/* Helpers for prepared statements and parameterised statements. + * + * See the connection class for more about such statements. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_PARAMS +#define PQXX_H_PARAMS + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/statement_parameters.hxx" +#include "pqxx/types.hxx" + + +/// @deprecated The new @ref params class replaces all of this. +namespace pqxx::prepare +{ +/// Pass a number of statement parameters only known at runtime. +/** @deprecated Use @ref params instead. + * + * When you call any of the `exec_params` functions, the number of arguments + * is normally known at compile time. This helper function supports the case + * where it is not. + * + * Use this function to pass a variable number of parameters, based on a + * sequence ranging from `begin` to `end` exclusively. + * + * The technique combines with the regular static parameters. You can use it + * to insert dynamic parameter lists in any place, or places, among the call's + * parameters. You can even insert multiple dynamic sequences. + * + * @param begin A pointer or iterator for iterating parameters. + * @param end A pointer or iterator for iterating parameters. + * @return An object representing the parameters. + */ +template +[[deprecated("Use the params class instead.")]] constexpr inline auto +make_dynamic_params(IT begin, IT end) +{ + return pqxx::internal::dynamic_params(begin, end); +} + + +/// Pass a number of statement parameters only known at runtime. +/** @deprecated Use @ref params instead. + * + * When you call any of the `exec_params` functions, the number of arguments + * is normally known at compile time. This helper function supports the case + * where it is not. + * + * Use this function to pass a variable number of parameters, based on a + * container of parameter values. + * + * The technique combines with the regular static parameters. You can use it + * to insert dynamic parameter lists in any place, or places, among the call's + * parameters. You can even insert multiple dynamic containers. + * + * @param container A container of parameter values. + * @return An object representing the parameters. + */ +template +[[deprecated("Use the params class instead.")]] constexpr inline auto +make_dynamic_params(C const &container) +{ + using IT = typename C::const_iterator; +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return pqxx::internal::dynamic_params{container}; +#include "pqxx/internal/ignore-deprecated-post.hxx" +} + + +/// Pass a number of statement parameters only known at runtime. +/** @deprecated Use @ref params instead. + * + * When you call any of the `exec_params` functions, the number of arguments + * is normally known at compile time. This helper function supports the case + * where it is not. + * + * Use this function to pass a variable number of parameters, based on a + * container of parameter values. + * + * The technique combines with the regular static parameters. You can use it + * to insert dynamic parameter lists in any place, or places, among the call's + * parameters. You can even insert multiple dynamic containers. + * + * @param container A container of parameter values. + * @param accessor For each parameter `p`, pass `accessor(p)`. + * @return An object representing the parameters. + */ +template +[[deprecated("Use the params class instead.")]] constexpr inline auto +make_dynamic_params(C &container, ACCESSOR accessor) +{ + using IT = decltype(std::begin(container)); +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return pqxx::internal::dynamic_params{container, accessor}; +#include "pqxx/internal/ignore-deprecated-post.hxx" +} +} // namespace pqxx::prepare + + +namespace pqxx +{ +/// Generate parameter placeholders for use in an SQL statement. +/** When you want to pass parameters to a prepared statement or a parameterised + * statement, you insert placeholders into the SQL. During invocation, the + * database replaces those with the respective parameter values you passed. + * + * The placeholders look like `$1` (for the first parameter value), `$2` (for + * the second), and so on. You can just write those directly in your + * statement. But for those rare cases where it becomes difficult to track + * which number a placeholder should have, you can use a `placeholders` object + * to count and generate them in order. + */ +template class placeholders +{ +public: + /// Maximum number of parameters we support. + static inline constexpr unsigned int max_params{ + (std::numeric_limits::max)()}; + + placeholders() + { + static constexpr auto initial{"$1\0"sv}; + initial.copy(std::data(m_buf), std::size(initial)); + } + + /// Read an ephemeral version of the current placeholder text. + /** @warning Changing the current placeholder number will overwrite this. + * Use the view immediately, or lose it. + */ + constexpr zview view() const &noexcept + { + return zview{std::data(m_buf), m_len}; + } + + /// Read the current placeholder text, as a `std::string`. + /** This will be slightly slower than converting to a `zview`. With most + * C++ implementations however, until you get into ridiculous numbers of + * parameters, the string will benefit from the Short String Optimization, or + * SSO. + */ + std::string get() const { return std::string(std::data(m_buf), m_len); } + + /// Move on to the next parameter. + void next() & + { + if (m_current >= max_params) + throw range_error{pqxx::internal::concat( + "Too many parameters in one statement: limit is ", max_params, ".")}; + ++m_current; + if (m_current % 10 == 0) + { + // Carry the 1. Don't get too clever for this relatively rare + // case, just rewrite the entire number. Leave the $ in place + // though. + char *const data{std::data(m_buf)}; + char *const end{string_traits::into_buf( + data + 1, data + std::size(m_buf), m_current)}; + // (Subtract because we don't include the trailing zero.) + m_len = check_cast(end - data, "placeholders counter") - 1; + } + else + { + PQXX_LIKELY + // Shortcut for the common case: just increment that last digit. + ++m_buf[m_len - 1]; + } + } + + /// Return the current placeholder number. The initial placeholder is 1. + COUNTER count() const noexcept { return m_current; } + +private: + /// Current placeholder number. Starts at 1. + COUNTER m_current = 1; + + /// Length of the current placeholder string, not including trailing zero. + COUNTER m_len = 2; + + /// Text buffer where we render the placeholders, with a trailing zero. + /** We keep reusing this for every subsequent placeholder, just because we + * don't like string allocations. + * + * Maximum length is the maximum base-10 digits that COUNTER can fully + * represent, plus 1 more for the extra digit that it can only partially + * fill up, plus room for the dollar sign and the trailing zero. + */ + std::array::digits10 + 3> m_buf; +}; + + +/// Build a parameter list for a parameterised or prepared statement. +/** When calling a parameterised statement or a prepared statement, you can + * pass parameters into the statement directly in the invocation, as + * additional arguments to `exec_prepared` or `exec_params`. But in + * complex cases, sometimes that's just not convenient. + * + * In those situations, you can create a `params` and append your parameters + * into that, one by one. Then you pass the `params` to `exec_prepared` or + * `exec_params`. + * + * Combinations also work: if you have a `params` containing a string + * parameter, and you call `exec_params` with an `int` argument followed by + * your `params`, you'll be passing the `int` as the first parameter and + * the string as the second. You can even insert a `params` in a `params`, + * or pass two `params` objects to a statement. + */ +class PQXX_LIBEXPORT params +{ +public: + params() = default; + + /// Pre-populate a `params` with `args`. Feel free to add more later. + template constexpr params(Args &&...args) + { + reserve(sizeof...(args)); + append_pack(std::forward(args)...); + } + + /// Pre-allocate room for at least `n` parameters. + /** This is not needed, but it may improve efficiency. + * + * Reserve space if you're going to add parameters individually, and you've + * got some idea of how many there are going to be. It may save some + * memory re-allocations. + */ + void reserve(std::size_t n) &; + + // C++20: constexpr. + /// Get the number of parameters currently in this `params`. + [[nodiscard]] auto size() const noexcept { return m_params.size(); } + + // C++20: Use the vector's ssize() directly and go noexcept+constexpr. + /// Get the number of parameters (signed). + /** Unlike `size()`, this is not yet `noexcept`. That's because C++17's + * `std::vector` does not have a `ssize()` member function. These member + * functions are `noexcept`, but `std::size()` and `std::ssize()` are + * not. + */ + [[nodiscard]] auto ssize() const { return pqxx::internal::ssize(m_params); } + + /// Append a null value. + void append() &; + + /// Append a non-null zview parameter. + /** The underlying data must stay valid for as long as the `params` + * remains active. + */ + void append(zview) &; + + /// Append a non-null string parameter. + /** Copies the underlying data into internal storage. For best efficiency, + * use the @ref zview variant if you can, or `std::move()` + */ + void append(std::string const &) &; + + /// Append a non-null string parameter. + void append(std::string &&) &; + + /// Append a non-null binary parameter. + /** The underlying data must stay valid for as long as the `params` + * remains active. + */ + void append(std::basic_string_view) &; + + /// Append a non-null binary parameter. + /** Copies the underlying data into internal storage. For best efficiency, + * use the `std::basic_string_view` variant if you can, or + * `std::move()`. + */ + void append(std::basic_string const &) &; + +#if defined(PQXX_HAVE_CONCEPTS) + /// Append a non-null binary parameter. + /** The `data` object must stay in place and unchanged, for as long as the + * `params` remains active. + */ + template void append(DATA const &data) & + { + append( + std::basic_string_view{std::data(data), std::size(data)}); + } +#endif // PQXX_HAVE_CONCEPTS + + /// Append a non-null binary parameter. + void append(std::basic_string &&) &; + + /// @deprecated Append binarystring parameter. + /** The binarystring must stay valid for as long as the `params` remains + * active. + */ + void append(binarystring const &value) &; + + /// Append all parameters from value. + template + void append(pqxx::internal::dynamic_params const &value) & + { + for (auto ¶m : value) append(value.access(param)); + } + + void append(params const &value) &; + + void append(params &&value) &; + + /// Append a non-null parameter, converting it to its string + /// representation. + template void append(TYPE const &value) & + { + // TODO: Pool storage for multiple string conversions in one buffer? + if constexpr (nullness>::always_null) + { + ignore_unused(value); + m_params.emplace_back(); + } + else if (is_null(value)) + { + m_params.emplace_back(); + } + else + { + m_params.emplace_back(entry{to_string(value)}); + } + } + + /// Append all elements of `range` as parameters. + template void append_multi(RANGE const &range) & + { +#if defined(PQXX_HAVE_CONCEPTS) + if constexpr (std::ranges::sized_range) + reserve(std::size(*this) + std::size(range)); +#endif + for (auto &value : range) append(value); + } + + /// For internal use: Generate a `params` object for use in calls. + /** The params object encapsulates the pointers which we will need to pass + * to libpq when calling a parameterised or prepared statement. + * + * The pointers in the params will refer to storage owned by either the + * params object, or the caller. This is not a problem because a + * `c_params` object is guaranteed to live only while the call is going on. + * As soon as we climb back out of that call tree, we're done with that + * data. + */ + pqxx::internal::c_params make_c_params() const; + +private: + /// Recursively append a pack of params. + template + void append_pack(Arg &&arg, More &&...args) + { + this->append(std::forward(arg)); + // Recurse for remaining args. + append_pack(std::forward(args)...); + } + + /// Terminating case: append an empty parameter pack. It's not hard BTW. + constexpr void append_pack() noexcept {} + + // The way we store a parameter depends on whether it's binary or text + // (most types are text), and whether we're responsible for storing the + // contents. + using entry = std::variant< + std::nullptr_t, zview, std::string, std::basic_string_view, + std::basic_string>; + std::vector m_params; + + static constexpr std::string_view s_overflow{ + "Statement parameter length overflow."sv}; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/pipeline b/ext/libpqxx-7.7.3/include/pqxx/pipeline new file mode 100644 index 000000000..bf828843a --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/pipeline @@ -0,0 +1,8 @@ +/** pqxx::pipeline class. + * + * Throughput-optimized query interface. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/pipeline.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/pipeline.hxx b/ext/libpqxx-7.7.3/include/pqxx/pipeline.hxx new file mode 100644 index 000000000..049dcdd58 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/pipeline.hxx @@ -0,0 +1,237 @@ +/* Definition of the pqxx::pipeline class. + * + * Throughput-optimized mechanism for executing queries. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/pipeline instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_PIPELINE +#define PQXX_H_PIPELINE + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include + +#include "pqxx/transaction_base.hxx" + + +namespace pqxx +{ +// TODO: libpq 14 introduced a similar "pipeline mode." Can we use that? + +/// Processes several queries in FIFO manner, optimized for high throughput. +/** Use a pipeline if you want to keep doing useful work while your queries are + * executing. Result retrieval is decoupled from execution request; queries + * "go in at the front" and results "come out the back." + * + * Actually, you can retrieve the results in any order if you want, but it may + * lead to surprising "time travel" effects if any of the queries fails. In + * particular, syntax errors in the queries can confuse things and show up too + * early in the stream of results. + * + * Generally, if any of the queries fails, it will throw an exception at the + * point where you request its result. But it may happen earlier, especially + * if you request results out of chronological order. + * + * @warning While a pipeline is active, you cannot execute queries, open + * streams, etc. on the same transaction. A transaction can have at most one + * object of a type derived from @ref pqxx::transaction_focus active on it at a + * time. + */ +class PQXX_LIBEXPORT pipeline : public transaction_focus +{ +public: + /// Identifying numbers for queries. + using query_id = long; + + pipeline(pipeline const &) = delete; + pipeline &operator=(pipeline const &) = delete; + + /// Start a pipeline. + explicit pipeline(transaction_base &t) : transaction_focus{t, s_classname} + { + init(); + } + /// Start a pipeline. Assign it a name, for more helpful error messages. + pipeline(transaction_base &t, std::string_view tname) : + transaction_focus{t, s_classname, tname} + { + init(); + } + + /// Close the pipeline. + ~pipeline() noexcept; + + /// Add query to the pipeline. + /** Queries accumulate in the pipeline, which sends them to the backend in a + * batch separated by semicolons. The queries you insert must not use this + * trick themselves, or the pipeline will get hopelessly confused! + * + * @return Identifier for this query, unique only within this pipeline. + */ + query_id insert(std::string_view) &; + + /// Wait for all ongoing or pending operations to complete, and detach. + /** Detaches from the transaction when done. + * + * This does not produce the queries' results, so it may not report any + * errors which may have occurred in their execution. To be sure that your + * statements succeeded, call @ref retrieve until the pipeline is empty. + */ + void complete(); + + /// Forget all ongoing or pending operations and retrieved results. + /** Queries already sent to the backend may still be completed, depending + * on implementation and timing. + * + * Any error state (unless caused by an internal error) will also be cleared. + * This is mostly useful in a nontransaction, since a backend transaction is + * aborted automatically when an error occurs. + * + * Detaches from the transaction when done. + */ + void flush(); + + /// Cancel ongoing query, if any. + /** May cancel any or all of the queries that have been inserted at this + * point whose results have not yet been retrieved. If the pipeline lives in + * a backend transaction, that transaction may be left in a nonfunctional + * state in which it can only be aborted. + * + * Therefore, either use this function in a nontransaction, or abort the + * transaction after calling it. + */ + void cancel(); + + /// Is result for given query available? + [[nodiscard]] bool is_finished(query_id) const; + + /// Retrieve result for given query. + /** If the query failed for whatever reason, this will throw an exception. + * The function will block if the query has not finished yet. + * @warning If results are retrieved out-of-order, i.e. in a different order + * than the one in which their queries were inserted, errors may "propagate" + * to subsequent queries. + */ + result retrieve(query_id qid) + { + return retrieve(m_queries.find(qid)).second; + } + + /// Retrieve oldest unretrieved result (possibly wait for one). + /** @return The query's identifier and its result set. */ + std::pair retrieve(); + + [[nodiscard]] bool empty() const noexcept { return std::empty(m_queries); } + + /// Set maximum number of queries to retain before issuing them to the + /// backend. + /** The pipeline will perform better if multiple queries are issued at once, + * but retaining queries until the results are needed (as opposed to issuing + * them to the backend immediately) may negate any performance benefits the + * pipeline can offer. + * + * Recommended practice is to set this value no higher than the number of + * queries you intend to insert at a time. + * @param retain_max A nonnegative "retention capacity;" passing zero will + * cause queries to be issued immediately + * @return Old retention capacity + */ + int retain(int retain_max = 2) &; + + + /// Resume retained query emission. Harmless when not needed. + void resume() &; + +private: + struct PQXX_PRIVATE Query + { + explicit Query(std::string_view q) : + query{std::make_shared(q)} + {} + + std::shared_ptr query; + result res; + }; + + using QueryMap = std::map; + + void init(); + void attach(); + void detach(); + + /// Upper bound to query id's. + static constexpr query_id qid_limit() noexcept + { + // Parenthesise this to work around an eternal Visual C++ problem: + // Without the extra parentheses, unless NOMINMAX is defined, the + // preprocessor will mistake this "max" for its annoying built-in macro + // of the same name. + return (std::numeric_limits::max)(); + } + + /// Create new query_id. + PQXX_PRIVATE query_id generate_id(); + + bool have_pending() const noexcept + { + return m_issuedrange.second != m_issuedrange.first; + } + + PQXX_PRIVATE void issue(); + + /// The given query failed; never issue anything beyond that. + void set_error_at(query_id qid) noexcept + { + PQXX_UNLIKELY + if (qid < m_error) + m_error = qid; + } + + /// Throw pqxx::internal_error. + [[noreturn]] PQXX_PRIVATE void internal_error(std::string const &err); + + PQXX_PRIVATE bool obtain_result(bool expect_none = false); + + PQXX_PRIVATE void obtain_dummy(); + PQXX_PRIVATE void get_further_available_results(); + PQXX_PRIVATE void check_end_results(); + + /// Receive any results that happen to be available; it's not urgent. + PQXX_PRIVATE void receive_if_available(); + + /// Receive results, up to stop if possible. + PQXX_PRIVATE void receive(pipeline::QueryMap::const_iterator stop); + std::pair retrieve(pipeline::QueryMap::iterator); + + QueryMap m_queries; + std::pair m_issuedrange; + int m_retain = 0; + int m_num_waiting = 0; + query_id m_q_id = 0; + + /// Is there a "dummy query" pending? + bool m_dummy_pending = false; + + /// Point at which an error occurred; no results beyond it will be available + query_id m_error = qid_limit(); + + /// Encoding. + /** We store this in the object to avoid the risk of exceptions at awkward + * moments. + */ + internal::encoding_group m_encoding; + + static constexpr std::string_view s_classname{"pipeline"}; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/pqxx b/ext/libpqxx-7.7.3/include/pqxx/pqxx new file mode 100644 index 000000000..17a8eaa9c --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/pqxx @@ -0,0 +1,28 @@ +/// Convenience header: include all libpqxx definitions. +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/array.hxx" +#include "pqxx/binarystring.hxx" +#include "pqxx/blob.hxx" +#include "pqxx/connection.hxx" +#include "pqxx/cursor.hxx" +#include "pqxx/errorhandler.hxx" +#include "pqxx/except.hxx" +#include "pqxx/largeobject.hxx" +#include "pqxx/nontransaction.hxx" +#include "pqxx/notification.hxx" +#include "pqxx/params.hxx" +#include "pqxx/pipeline.hxx" +#include "pqxx/prepared_statement.hxx" +#include "pqxx/result.hxx" +#include "pqxx/internal/result_iterator.hxx" +#include "pqxx/internal/result_iter.hxx" +#include "pqxx/robusttransaction.hxx" +#include "pqxx/row.hxx" +#include "pqxx/stream_from.hxx" +#include "pqxx/stream_to.hxx" +#include "pqxx/subtransaction.hxx" +#include "pqxx/transaction.hxx" +#include "pqxx/transactor.hxx" + +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/prepared_statement b/ext/libpqxx-7.7.3/include/pqxx/prepared_statement new file mode 100644 index 000000000..674be7090 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/prepared_statement @@ -0,0 +1,3 @@ +/// @deprecated Include @c instead. + +#include "params.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/prepared_statement.hxx b/ext/libpqxx-7.7.3/include/pqxx/prepared_statement.hxx new file mode 100644 index 000000000..674be7090 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/prepared_statement.hxx @@ -0,0 +1,3 @@ +/// @deprecated Include @c instead. + +#include "params.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/range b/ext/libpqxx-7.7.3/include/pqxx/range new file mode 100644 index 000000000..11985eca4 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/range @@ -0,0 +1,6 @@ +/** Client-side support for SQL range types. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/range.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/range.hxx b/ext/libpqxx-7.7.3/include/pqxx/range.hxx new file mode 100644 index 000000000..dc480e4b7 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/range.hxx @@ -0,0 +1,515 @@ +#ifndef PQXX_H_RANGE +#define PQXX_H_RANGE + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#include "pqxx/internal/array-composite.hxx" +#include "pqxx/internal/concat.hxx" + +namespace pqxx +{ +/// An _unlimited_ boundary value to a @ref pqxx::range. +/** Use this as a lower or upper bound for a range if the range should extend + * to infinity on that side. + * + * An unlimited boundary is always inclusive of "infinity" values, if the + * range's value type supports them. + */ +struct no_bound +{ + template constexpr bool extends_down_to(TYPE const &) const + { + return true; + } + template constexpr bool extends_up_to(TYPE const &) const + { + return true; + } +}; + + +/// An _inclusive_ boundary value to a @ref pqxx::range. +/** Use this as a lower or upper bound for a range if the range should include + * the value. + */ +template class inclusive_bound +{ +public: + inclusive_bound() = delete; + explicit inclusive_bound(TYPE const &value) : m_value{value} + { + if (is_null(value)) + throw argument_error{"Got null value as an inclusive range bound."}; + } + + [[nodiscard]] constexpr TYPE const &get() const &noexcept { return m_value; } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + /// Would this bound, as a lower bound, include value? + [[nodiscard]] bool extends_down_to(TYPE const &value) const + { + return not(value < m_value); + } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + /// Would this bound, as an upper bound, include value? + [[nodiscard]] bool extends_up_to(TYPE const &value) const + { + return not(m_value < value); + } + +private: + TYPE m_value; +}; + + +/// An _exclusive_ boundary value to a @ref pqxx::range. +/** Use this as a lower or upper bound for a range if the range should _not_ + * include the value. + */ +template class exclusive_bound +{ +public: + exclusive_bound() = delete; + explicit exclusive_bound(TYPE const &value) : m_value{value} + { + if (is_null(value)) + throw argument_error{"Got null value as an exclusive range bound."}; + } + + [[nodiscard]] constexpr TYPE const &get() const &noexcept { return m_value; } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + /// Would this bound, as a lower bound, include value? + [[nodiscard]] bool extends_down_to(TYPE const &value) const + { + return m_value < value; + } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + /// Would this bound, as an upper bound, include value? + [[nodiscard]] bool extends_up_to(TYPE const &value) const + { + return value < m_value; + } + +private: + TYPE m_value; +}; + + +/// A range boundary value. +/** A range bound is either no bound at all; or an inclusive bound; or an + * exclusive bound. Pass one of the three to the constructor. + */ +template class range_bound +{ +public: + range_bound() = delete; + // TODO: constexpr and/or noexcept if underlying constructor supports it. + range_bound(no_bound) : m_bound{} {} + // TODO: constexpr and/or noexcept if underlying constructor supports it. + range_bound(inclusive_bound const &bound) : m_bound{bound} {} + // TODO: constexpr and/or noexcept if underlying constructor supports it. + range_bound(exclusive_bound const &bound) : m_bound{bound} {} + // TODO: constexpr and/or noexcept if underlying constructor supports it. + range_bound(range_bound const &) = default; + // TODO: constexpr and/or noexcept if underlying constructor supports it. + range_bound(range_bound &&) = default; + + // TODO: constexpr and/or noexcept if underlying operators support it. + bool operator==(range_bound const &rhs) const + { + if (this->is_limited()) + return ( + rhs.is_limited() and (this->is_inclusive() == rhs.is_inclusive()) and + (*this->value() == *rhs.value())); + else + return not rhs.is_limited(); + } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + bool operator!=(range_bound const &rhs) const { return not(*this == rhs); } + range_bound &operator=(range_bound const &) = default; + range_bound &operator=(range_bound &&) = default; + + /// Is this a finite bound? + constexpr bool is_limited() const noexcept + { + return not std::holds_alternative(m_bound); + } + + /// Is this boundary an inclusive one? + constexpr bool is_inclusive() const noexcept + { + return std::holds_alternative>(m_bound); + } + + /// Is this boundary an exclusive one? + constexpr bool is_exclusive() const noexcept + { + return std::holds_alternative>(m_bound); + } + + // TODO: constexpr/noexcept if underlying function supports it. + /// Would this bound, as a lower bound, include `value`? + bool extends_down_to(TYPE const &value) const + { + return std::visit( + [&value](auto const &bound) { return bound.extends_down_to(value); }, + m_bound); + } + + // TODO: constexpr/noexcept if underlying function supports it. + /// Would this bound, as an upper bound, include `value`? + bool extends_up_to(TYPE const &value) const + { + return std::visit( + [&value](auto const &bound) { return bound.extends_up_to(value); }, + m_bound); + } + + /// Return bound value, or `nullptr` if it's not limited. + [[nodiscard]] constexpr TYPE const *value() const &noexcept + { + return std::visit( + [](auto const &bound) noexcept { + using bound_t = std::decay_t; + if constexpr (std::is_same_v) + return static_cast(nullptr); + else + return &bound.get(); + }, + m_bound); + } + +private: + std::variant, exclusive_bound> m_bound; +}; + + +// C++20: Concepts for comparisons, construction, etc. +/// A C++ equivalent to PostgreSQL's range types. +/** You can use this as a client-side representation of a "range" in SQL. + * + * PostgreSQL defines several range types, differing in the data type over + * which they range. You can also define your own range types. + * + * Usually you'll want the server to deal with ranges. But on occasions where + * you need to work with them client-side, you may want to use @ref + * pqxx::range. (In cases where all you do is pass them along to the server + * though, it's not worth the complexity. In that case you might as well treat + * ranges as just strings.) + * + * For documentation on PostgreSQL's range types, see: + * https://www.postgresql.org/docs/current/rangetypes.html + * + * The value type must be copyable and default-constructible, and support the + * less-than (`<`) and equals (`==`) comparisons. Value initialisation must + * produce a consistent value. + */ +template class range +{ +public: + /// Create a range. + /** For each of the two bounds, pass a @ref no_bound, @ref inclusive_bound, + * or + * @ref exclusive_bound. + */ + range(range_bound lower, range_bound upper) : + m_lower{lower}, m_upper{upper} + { + if ( + lower.is_limited() and upper.is_limited() and + (*upper.value() < *lower.value())) + throw range_error{internal::concat( + "Range's lower bound (", *lower.value(), + ") is greater than its upper bound (", *upper.value(), ").")}; + } + + // TODO: constexpr and/or noexcept if underlying constructor supports it. + /// Create an empty range. + /** SQL has a separate literal to denote an empty range, but any range which + * encompasses no values is an empty range. + */ + range() : + m_lower{exclusive_bound{TYPE{}}}, + m_upper{exclusive_bound{TYPE{}}} + {} + + // TODO: constexpr and/or noexcept if underlying operators support it. + bool operator==(range const &rhs) const + { + return (this->lower_bound() == rhs.lower_bound() and + this->upper_bound() == rhs.upper_bound()) or + (this->empty() and rhs.empty()); + } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + bool operator!=(range const &rhs) const { return !(*this == rhs); } + + range(range const &) = default; + range(range &&) = default; + range &operator=(range const &) = default; + range &operator=(range &&) = default; + + // TODO: constexpr and/or noexcept if underlying operator supports it. + /// Is this range clearly empty? + /** An empty range encompasses no values. + * + * It is possible to "fool" this. For example, if your range is of an + * integer type and has exclusive bounds of 0 and 1, it encompasses no values + * but its `empty()` will return false. The PostgreSQL implementation, by + * contrast, will notice that it is empty. Similar things can happen for + * floating-point types, but with more subtleties and edge cases. + */ + bool empty() const + { + return (m_lower.is_exclusive() or m_upper.is_exclusive()) and + m_lower.is_limited() and m_upper.is_limited() and + not(*m_lower.value() < *m_upper.value()); + } + + // TODO: constexpr and/or noexcept if underlying functions support it. + /// Does this range encompass `value`? + bool contains(TYPE value) const + { + return m_lower.extends_down_to(value) and m_upper.extends_up_to(value); + } + + // TODO: constexpr and/or noexcept if underlying operators support it. + /// Does this range encompass all of `other`? + /** This function is not particularly smart. It does not know, for example, + * that integer ranges `[0,9]` and `[0,10)` contain the same values. + */ + bool contains(range const &other) const + { + return (*this & other) == other; + } + + [[nodiscard]] constexpr range_bound const & + lower_bound() const &noexcept + { + return m_lower; + } + [[nodiscard]] constexpr range_bound const & + upper_bound() const &noexcept + { + return m_upper; + } + + // TODO: constexpr and/or noexcept if underlying operators support it. + /// Intersection of two ranges. + /** Returns a range describing those values which are in both ranges. + */ + range operator&(range const &other) const + { + range_bound lower{no_bound{}}; + if (not this->lower_bound().is_limited()) + lower = other.lower_bound(); + else if (not other.lower_bound().is_limited()) + lower = this->lower_bound(); + else if (*this->lower_bound().value() < *other.lower_bound().value()) + lower = other.lower_bound(); + else if (*other.lower_bound().value() < *this->lower_bound().value()) + lower = this->lower_bound(); + else if (this->lower_bound().is_exclusive()) + lower = this->lower_bound(); + else + lower = other.lower_bound(); + + range_bound upper{no_bound{}}; + if (not this->upper_bound().is_limited()) + upper = other.upper_bound(); + else if (not other.upper_bound().is_limited()) + upper = this->upper_bound(); + else if (*other.upper_bound().value() < *this->upper_bound().value()) + upper = other.upper_bound(); + else if (*this->upper_bound().value() < *other.upper_bound().value()) + upper = this->upper_bound(); + else if (this->upper_bound().is_exclusive()) + upper = this->upper_bound(); + else + upper = other.upper_bound(); + + if ( + lower.is_limited() and upper.is_limited() and + (*upper.value() < *lower.value())) + return {}; + else + return {lower, upper}; + } + + /// Convert to another base type. + template operator range() const + { + range_bound lower{no_bound{}}, upper{no_bound{}}; + if (lower_bound().is_inclusive()) + lower = inclusive_bound{*lower_bound().value()}; + else if (lower_bound().is_exclusive()) + lower = exclusive_bound{*lower_bound().value()}; + + if (upper_bound().is_inclusive()) + upper = inclusive_bound{*upper_bound().value()}; + else if (upper_bound().is_exclusive()) + upper = exclusive_bound{*upper_bound().value()}; + + return {lower, upper}; + } + +private: + range_bound m_lower, m_upper; +}; + + +/// String conversions for a @ref range type. +/** Conversion assumes that either your client encoding is UTF-8, or the values + * are pure ASCII. + */ +template struct string_traits> +{ + [[nodiscard]] static inline zview + to_buf(char *begin, char *end, range const &value) + { + return generic_to_buf(begin, end, value); + } + + static inline char * + into_buf(char *begin, char *end, range const &value) + { + if (value.empty()) + { + if ((end - begin) <= internal::ssize(s_empty)) + throw conversion_overrun{s_overrun.c_str()}; + char *here = begin + s_empty.copy(begin, std::size(s_empty)); + *here++ = '\0'; + return here; + } + else + { + if (end - begin < 4) + throw conversion_overrun{s_overrun.c_str()}; + char *here = begin; + *here++ = + (static_cast(value.lower_bound().is_inclusive() ? '[' : '(')); + TYPE const *lower{value.lower_bound().value()}; + // Convert bound (but go back to overwrite that trailing zero). + if (lower != nullptr) + here = string_traits::into_buf(here, end, *lower) - 1; + *here++ = ','; + TYPE const *upper{value.upper_bound().value()}; + // Convert bound (but go back to overwrite that trailing zero). + if (upper != nullptr) + here = string_traits::into_buf(here, end, *upper) - 1; + if ((end - here) < 2) + throw conversion_overrun{s_overrun.c_str()}; + *here++ = + static_cast(value.upper_bound().is_inclusive() ? ']' : ')'); + *here++ = '\0'; + return here; + } + } + + [[nodiscard]] static inline range from_string(std::string_view text) + { + if (std::size(text) < 3) + throw pqxx::conversion_error{err_bad_input(text)}; + bool left_inc{false}; + switch (text[0]) + { + case '[': left_inc = true; break; + + case '(': break; + + case 'e': + case 'E': + if ( + (std::size(text) != std::size(s_empty)) or + (text[1] != 'm' and text[1] != 'M') or + (text[2] != 'p' and text[2] != 'P') or + (text[3] != 't' and text[3] != 'T') or + (text[4] != 'y' and text[4] != 'Y')) + throw pqxx::conversion_error{err_bad_input(text)}; + return {}; + break; + + default: throw pqxx::conversion_error{err_bad_input(text)}; + } + + auto scan{internal::get_glyph_scanner(internal::encoding_group::UTF8)}; + // The field parser uses this to track which field it's parsing, and + // when not to expect a field separator. + std::size_t index{0}; + // The last field we expect to see. + static constexpr std::size_t last{1}; + // Current parsing position. We skip the opening parenthesis or bracket. + std::size_t pos{1}; + // The string may leave out either bound to indicate that it's unlimited. + std::optional lower, upper; + // We reuse the same field parser we use for composite values and arrays. + internal::parse_composite_field(index, text, pos, lower, scan, last); + internal::parse_composite_field(index, text, pos, upper, scan, last); + + // We need one more character: the closing parenthesis or bracket. + if (pos != std::size(text)) + throw pqxx::conversion_error{err_bad_input(text)}; + char const closing{text[pos - 1]}; + if (closing != ')' and closing != ']') + throw pqxx::conversion_error{err_bad_input(text)}; + bool const right_inc{closing == ']'}; + + range_bound lower_bound{no_bound{}}, upper_bound{no_bound{}}; + if (lower) + { + if (left_inc) + lower_bound = inclusive_bound{*lower}; + else + lower_bound = exclusive_bound{*lower}; + } + if (upper) + { + if (right_inc) + upper_bound = inclusive_bound{*upper}; + else + upper_bound = exclusive_bound{*upper}; + } + + return {lower_bound, upper_bound}; + } + + [[nodiscard]] static inline constexpr std::size_t + size_buffer(range const &value) noexcept + { + TYPE const *lower{value.lower_bound().value()}, + *upper{value.upper_bound().value()}; + std::size_t const lsz{ + lower == nullptr ? 0 : string_traits::size_buffer(*lower) - 1}, + usz{upper == nullptr ? 0 : string_traits::size_buffer(*upper) - 1}; + + if (value.empty()) + return std::size(s_empty) + 1; + else + return 1 + lsz + 1 + usz + 2; + } + +private: + static constexpr zview s_empty{"empty"_zv}; + static constexpr auto s_overrun{"Not enough space in buffer for range."_zv}; + + /// Compose error message for invalid range input. + static std::string err_bad_input(std::string_view text) + { + return internal::concat("Invalid range input: '", text, "'"); + } +}; + + +/// A range type does not have an innate null value. +template struct nullness> : no_null> +{}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/result b/ext/libpqxx-7.7.3/include/pqxx/result new file mode 100644 index 000000000..523394b72 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/result @@ -0,0 +1,16 @@ +/** pqxx::result class and support classes. + * + * pqxx::result represents the set of result rows from a database query. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/result.hxx" + +// Now include some types which depend on result, but which the user will +// expect to see defined after including this header. +#include "pqxx/internal/result_iterator.hxx" +#include "pqxx/field.hxx" +#include "pqxx/internal/result_iter.hxx" + +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/result.hxx b/ext/libpqxx-7.7.3/include/pqxx/result.hxx new file mode 100644 index 000000000..6c41cc096 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/result.hxx @@ -0,0 +1,335 @@ +/* Definitions for the pqxx::result class and support classes. + * + * pqxx::result represents the set of result rows from a database query. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/result instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_RESULT +#define PQXX_H_RESULT + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include +#include + +#include "pqxx/except.hxx" +#include "pqxx/types.hxx" +#include "pqxx/util.hxx" +#include "pqxx/zview.hxx" + +#include "pqxx/internal/encodings.hxx" + + +namespace pqxx::internal +{ +// TODO: Make noexcept (but breaks ABI). +PQXX_LIBEXPORT void clear_result(pq::PGresult const *); +} // namespace pqxx::internal + + +namespace pqxx::internal::gate +{ +class result_connection; +class result_creation; +class result_pipeline; +class result_row; +class result_sql_cursor; +} // namespace pqxx::internal::gate + + +namespace pqxx +{ +/// Result set containing data returned by a query or command. +/** This behaves as a container (as defined by the C++ standard library) and + * provides random access const iterators to iterate over its rows. You can + * also access a row by indexing a `result R` by the row's zero-based + * number: + * + * + * for (result::size_type i=0; i < std::size(R); ++i) Process(R[i]); + * + * + * Result sets in libpqxx are lightweight, reference-counted wrapper objects + * which are relatively small and cheap to copy. Think of a result object as + * a "smart pointer" to an underlying result set. + * + * @warning The result set that a result object points to is not thread-safe. + * If you copy a result object, it still refers to the same underlying result + * set. So never copy, destroy, query, or otherwise access a result while + * another thread may be copying, destroying, querying, or otherwise accessing + * the same result set--even if it is doing so through a different result + * object! + */ +class PQXX_LIBEXPORT result +{ +public: + using size_type = result_size_type; + using difference_type = result_difference_type; + using reference = row; + using const_iterator = const_result_iterator; + using pointer = const_iterator; + using iterator = const_iterator; + using const_reverse_iterator = const_reverse_result_iterator; + using reverse_iterator = const_reverse_iterator; + + result() noexcept : + m_data{make_data_pointer()}, + m_query{}, + m_encoding{internal::encoding_group::MONOBYTE} + {} + + result(result const &rhs) noexcept = default; + result(result &&rhs) noexcept = default; + + /// Assign one result to another. + /** Copying results is cheap: it copies only smart pointers, but the actual + * data stays in the same place. + */ + result &operator=(result const &rhs) noexcept = default; + + /// Assign one result to another, invaliding the old one. + result &operator=(result &&rhs) noexcept = default; + + /** + * @name Comparisons + * + * You can compare results for equality. Beware: this is a very strict, + * dumb comparison. The smallest difference between two results (such as a + * string "Foo" versus a string "foo") will make them unequal. + */ + //@{ + /// Compare two results for equality. + [[nodiscard]] bool operator==(result const &) const noexcept; + /// Compare two results for inequality. + [[nodiscard]] bool operator!=(result const &rhs) const noexcept + { + return not operator==(rhs); + } + //@} + + /// Iterate rows, reading them directly into a tuple of "TYPE...". + /** Converts the fields to values of the given respective types. + * + * Use this only with a ranged "for" loop. The iteration produces + * std::tuple which you can "unpack" to a series of `auto` + * variables. + */ + template auto iter() const; + + [[nodiscard]] const_reverse_iterator rbegin() const; + [[nodiscard]] const_reverse_iterator crbegin() const; + [[nodiscard]] const_reverse_iterator rend() const; + [[nodiscard]] const_reverse_iterator crend() const; + + [[nodiscard]] const_iterator begin() const noexcept; + [[nodiscard]] const_iterator cbegin() const noexcept; + [[nodiscard]] inline const_iterator end() const noexcept; + [[nodiscard]] inline const_iterator cend() const noexcept; + + [[nodiscard]] reference front() const noexcept; + [[nodiscard]] reference back() const noexcept; + + [[nodiscard]] PQXX_PURE size_type size() const noexcept; + [[nodiscard]] PQXX_PURE bool empty() const noexcept; + [[nodiscard]] size_type capacity() const noexcept { return size(); } + + /// Exchange two `result` values in an exception-safe manner. + /** If the swap fails, the two values will be exactly as they were before. + * + * The swap is not necessarily thread-safe. + */ + void swap(result &) noexcept; + + /// Index a row by number. + /** This returns a @ref row object. Generally you should not keep the row + * around as a variable, but if you do, make sure that your variable is a + * `row`, not a `row&`. + */ + [[nodiscard]] row operator[](size_type i) const noexcept; + +#if defined(PQXX_HAVE_MULTIDIMENSIONAL_SUBSCRIPT) + // TODO: If C++23 will let us, also accept string for the column. + [[nodiscard]] field + operator[](size_type row_num, row_size_type col_num) const noexcept; +#endif + + /// Index a row by number, but check that the row number is valid. + row at(size_type) const; + + /// Index a field by row number and column number. + field at(size_type, row_size_type) const; + + /// Let go of the result's data. + /** Use this if you need to deallocate the result data earlier than you can + * destroy the `result` object itself. + * + * Multiple `result` objects can refer to the same set of underlying data. + * The underlying data will be deallocated once all `result` objects that + * refer to it are cleared or destroyed. + */ + void clear() noexcept + { + m_data.reset(); + m_query = nullptr; + } + + /** + * @name Column information + */ + //@{ + /// Number of columns in result. + [[nodiscard]] PQXX_PURE row_size_type columns() const noexcept; + + /// Number of given column (throws exception if it doesn't exist). + [[nodiscard]] row_size_type column_number(zview name) const; + + /// Name of column with this number (throws exception if it doesn't exist) + [[nodiscard]] char const *column_name(row_size_type number) const &; + + /// Return column's type, as an OID from the system catalogue. + [[nodiscard]] oid column_type(row_size_type col_num) const; + + /// Return column's type, as an OID from the system catalogue. + [[nodiscard]] oid column_type(zview col_name) const + { + return column_type(column_number(col_name)); + } + + /// What table did this column come from? + [[nodiscard]] oid column_table(row_size_type col_num) const; + + /// What table did this column come from? + [[nodiscard]] oid column_table(zview col_name) const + { + return column_table(column_number(col_name)); + } + + /// What column in its table did this column come from? + [[nodiscard]] row_size_type table_column(row_size_type col_num) const; + + /// What column in its table did this column come from? + [[nodiscard]] row_size_type table_column(zview col_name) const + { + return table_column(column_number(col_name)); + } + //@} + + /// Query that produced this result, if available (empty string otherwise) + [[nodiscard]] PQXX_PURE std::string const &query() const &noexcept; + + /// If command was an `INSERT` of 1 row, return oid of the inserted row. + /** @return Identifier of inserted row if exactly one row was inserted, or + * @ref oid_none otherwise. + */ + [[nodiscard]] PQXX_PURE oid inserted_oid() const; + + /// If command was `INSERT`, `UPDATE`, or `DELETE`: number of affected rows. + /** @return Number of affected rows if last command was `INSERT`, `UPDATE`, + * or `DELETE`; zero for all other commands. + */ + [[nodiscard]] PQXX_PURE size_type affected_rows() const; + + // C++20: Concept like std::invocable, but without specifying param types. + /// Run `func` on each row, passing the row's fields as parameters. + /** Goes through the rows from first to last. You provide a callable `func`. + * + * For each row in the `result`, `for_each` will call `func`. It converts + * the row's fields to the types of `func`'s parameters, and pass them to + * `func`. + * + * (Therefore `func` must have a _single_ signature. It can't be a generic + * lambda, or an object of a class with multiple overloaded function call + * operators. Otherwise, `for_each` will have no way to detect a parameter + * list without ambiguity.) + * + * If any of your parameter types is `std::string_view`, it refers to the + * underlying storage of this `result`. + * + * If any of your parameter types is a reference type, its argument will + * refer to a temporary value which only lives for the duration of that + * single invocation to `func`. If the reference is an lvalue reference, it + * must be `const`. + * + * For example, this queries employee names and salaries from the database + * and prints how much each would like to earn instead: + * ```cxx + * tx.exec("SELECT name, salary FROM employee").for_each( + * [](std::string_view name, float salary){ + * std::cout << name << " would like " << salary * 2 << ".\n"; + * }) + * ``` + * + * If `func` throws an exception, processing stops at that point and + * propagates the exception. + * + * @throws usage_error if `func`'s number of parameters does not match the + * number of columns in this result. + */ + template inline void for_each(CALLABLE &&func) const; + +private: + using data_pointer = std::shared_ptr; + + /// Underlying libpq result set. + data_pointer m_data; + + /// Factory for data_pointer. + static data_pointer + make_data_pointer(internal::pq::PGresult const *res = nullptr) noexcept + { + return {res, internal::clear_result}; + } + + friend class pqxx::internal::gate::result_pipeline; + PQXX_PURE std::shared_ptr query_ptr() const noexcept + { + return m_query; + } + + /// Query string. + std::shared_ptr m_query; + + internal::encoding_group m_encoding; + + static std::string const s_empty_string; + + friend class pqxx::field; + // TODO: noexcept. Breaks ABI. + PQXX_PURE char const *get_value(size_type row, row_size_type col) const; + // TODO: noexcept. Breaks ABI. + PQXX_PURE bool get_is_null(size_type row, row_size_type col) const; + PQXX_PURE + field_size_type get_length(size_type, row_size_type) const noexcept; + + friend class pqxx::internal::gate::result_creation; + result( + internal::pq::PGresult *rhs, std::shared_ptr query, + internal::encoding_group enc); + + PQXX_PRIVATE void check_status(std::string_view desc = ""sv) const; + + friend class pqxx::internal::gate::result_connection; + friend class pqxx::internal::gate::result_row; + bool operator!() const noexcept { return m_data.get() == nullptr; } + operator bool() const noexcept { return m_data.get() != nullptr; } + + [[noreturn]] PQXX_PRIVATE void + throw_sql_error(std::string const &Err, std::string const &Query) const; + PQXX_PRIVATE PQXX_PURE int errorposition() const; + PQXX_PRIVATE std::string status_error() const; + + friend class pqxx::internal::gate::result_sql_cursor; + PQXX_PURE char const *cmd_status() const noexcept; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/robusttransaction b/ext/libpqxx-7.7.3/include/pqxx/robusttransaction new file mode 100644 index 000000000..04b71d7cc --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/robusttransaction @@ -0,0 +1,8 @@ +/** pqxx::robusttransaction class. + * + * pqxx::robusttransaction is a slower but safer transaction class. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/robusttransaction.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/robusttransaction.hxx b/ext/libpqxx-7.7.3/include/pqxx/robusttransaction.hxx new file mode 100644 index 000000000..faf6dbf5e --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/robusttransaction.hxx @@ -0,0 +1,120 @@ +/* Definition of the pqxx::robusttransaction class. + * + * pqxx::robusttransaction is a slower but safer transaction class. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/robusttransaction instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ROBUSTTRANSACTION +#define PQXX_H_ROBUSTTRANSACTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/dbtransaction.hxx" + +namespace pqxx::internal +{ +/// Helper base class for the @ref robusttransaction class template. +class PQXX_LIBEXPORT PQXX_NOVTABLE basic_robusttransaction + : public dbtransaction +{ +public: + virtual ~basic_robusttransaction() override = 0; + +protected: + basic_robusttransaction( + connection &c, zview begin_command, std::string_view tname); + basic_robusttransaction(connection &c, zview begin_command); + +private: + using IDType = unsigned long; + + std::string m_conn_string; + std::string m_xid; + int m_backendpid = -1; + + void init(zview begin_command); + + // @warning This function will become `final`. + virtual void do_commit() override; +}; +} // namespace pqxx::internal + + +namespace pqxx +{ +/** + * @ingroup transactions + * + * @{ + */ + +/// Slightly slower, better-fortified version of transaction. +/** Requires PostgreSQL 10 or better. + * + * robusttransaction is similar to transaction, but spends more time and effort + * to deal with the hopefully rare case that the connection to the backend is + * lost just while it's trying to commit. In such cases, the client does not + * know whether the backend (on the other side of the broken connection) + * managed to commit the transaction. + * + * When this happens, robusttransaction tries to reconnect to the database and + * figure out what happened. + * + * This service level was made optional since you may not want to pay the + * overhead where it is not necessary. Certainly the use of this class makes + * no sense for local connections, or for transactions that read the database + * but never modify it, or for noncritical database manipulations. + * + * Besides being slower, it's also more complex. Which means that in practice + * a robusttransaction could actually fail more instead of less often than a + * normal transaction. What robusttransaction tries to achieve is to give you + * certainty, not just be more successful per se. + */ +template +class robusttransaction final : public internal::basic_robusttransaction +{ +public: + /** Create robusttransaction of given name. + * @param c Connection inside which this robusttransaction should live. + * @param tname optional human-readable name for this transaction. + */ + robusttransaction(connection &c, std::string_view tname) : + internal::basic_robusttransaction{ + c, pqxx::internal::begin_cmd, + tname} + {} + + /** Create robusttransaction of given name. + * @param c Connection inside which this robusttransaction should live. + * @param tname optional human-readable name for this transaction. + */ + robusttransaction(connection &c, std::string &&tname) : + internal::basic_robusttransaction{ + c, pqxx::internal::begin_cmd, + std::move(tname)} + {} + + /** Create robusttransaction of given name. + * @param c Connection inside which this robusttransaction should live. + */ + explicit robusttransaction(connection &c) : + internal::basic_robusttransaction{ + c, pqxx::internal::begin_cmd} + {} + + virtual ~robusttransaction() noexcept override { close(); } +}; + +/** + * @} + */ +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/row b/ext/libpqxx-7.7.3/include/pqxx/row new file mode 100644 index 000000000..62a950ac8 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/row @@ -0,0 +1,11 @@ +/** pqxx::row class. + * + * pqxx::row refers to a row in a result. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/result.hxx" +#include "pqxx/row.hxx" + +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/row.hxx b/ext/libpqxx-7.7.3/include/pqxx/row.hxx new file mode 100644 index 000000000..5be5132e3 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/row.hxx @@ -0,0 +1,561 @@ +/* Definitions for the pqxx::result class and support classes. + * + * pqxx::result represents the set of result rows from a database query. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/result instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ROW +#define PQXX_H_ROW + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/except.hxx" +#include "pqxx/field.hxx" +#include "pqxx/result.hxx" + +#include "pqxx/internal/concat.hxx" + +namespace pqxx::internal +{ +template class result_iter; +} // namespace pqxx::internal + + +namespace pqxx +{ +/// Reference to one row in a result. +/** A row represents one row (also called a row) in a query result set. + * It also acts as a container mapping column numbers or names to field + * values (see below): + * + * ```cxx + * cout << row["date"].c_str() << ": " << row["name"].c_str() << endl; + * ``` + * + * The row itself acts like a (non-modifyable) container, complete with its + * own const_iterator and const_reverse_iterator. + */ +class PQXX_LIBEXPORT row +{ +public: + using size_type = row_size_type; + using difference_type = row_difference_type; + using const_iterator = const_row_iterator; + using iterator = const_iterator; + using reference = field; + using pointer = const_row_iterator; + using const_reverse_iterator = const_reverse_row_iterator; + using reverse_iterator = const_reverse_iterator; + + row() noexcept = default; + row(row &&) noexcept = default; + row(row const &) noexcept = default; + row &operator=(row const &) noexcept = default; + row &operator=(row &&) noexcept = default; + + /** + * @name Comparison + */ + //@{ + [[nodiscard]] PQXX_PURE bool operator==(row const &) const noexcept; + [[nodiscard]] bool operator!=(row const &rhs) const noexcept + { + return not operator==(rhs); + } + //@} + + [[nodiscard]] const_iterator begin() const noexcept; + [[nodiscard]] const_iterator cbegin() const noexcept; + [[nodiscard]] const_iterator end() const noexcept; + [[nodiscard]] const_iterator cend() const noexcept; + + /** + * @name Field access + */ + //@{ + [[nodiscard]] reference front() const noexcept; + [[nodiscard]] reference back() const noexcept; + + // TODO: noexcept. Breaks ABI. + [[nodiscard]] const_reverse_row_iterator rbegin() const; + // TODO: noexcept. Breaks ABI. + [[nodiscard]] const_reverse_row_iterator crbegin() const; + // TODO: noexcept. Breaks ABI. + [[nodiscard]] const_reverse_row_iterator rend() const; + // TODO: noexcept. Breaks ABI. + [[nodiscard]] const_reverse_row_iterator crend() const; + + [[nodiscard]] reference operator[](size_type) const noexcept; + /** Address field by name. + * @warning This is much slower than indexing by number, or iterating. + */ + [[nodiscard]] reference operator[](zview col_name) const; + + reference at(size_type) const; + /** Address field by name. + * @warning This is much slower than indexing by number, or iterating. + */ + reference at(zview col_name) const; + + [[nodiscard]] constexpr size_type size() const noexcept + { + return m_end - m_begin; + } + + [[deprecated("Swap iterators, not rows.")]] void swap(row &) noexcept; + + /// Row number, assuming this is a real row and not end()/rend(). + [[nodiscard]] constexpr result::size_type rownumber() const noexcept + { + return m_index; + } + + /** + * @name Column information + */ + //@{ + /// Number of given column (throws exception if it doesn't exist). + [[nodiscard]] size_type column_number(zview col_name) const; + + /// Return a column's type. + [[nodiscard]] oid column_type(size_type) const; + + /// Return a column's type. + [[nodiscard]] oid column_type(zview col_name) const + { + return column_type(column_number(col_name)); + } + + /// What table did this column come from? + [[nodiscard]] oid column_table(size_type col_num) const; + + /// What table did this column come from? + [[nodiscard]] oid column_table(zview col_name) const + { + return column_table(column_number(col_name)); + } + + /// What column number in its table did this result column come from? + /** A meaningful answer can be given only if the column in question comes + * directly from a column in a table. If the column is computed in any + * other way, a logic_error will be thrown. + * + * @param col_num a zero-based column number in this result set + * @return a zero-based column number in originating table + */ + [[nodiscard]] size_type table_column(size_type) const; + + /// What column number in its table did this result column come from? + [[nodiscard]] size_type table_column(zview col_name) const + { + return table_column(column_number(col_name)); + } + //@} + + [[nodiscard]] constexpr result::size_type num() const noexcept + { + return rownumber(); + } + + /** Produce a slice of this row, containing the given range of columns. + * + * @deprecated I haven't heard of anyone caring about row slicing at all in + * at least the last 15 years. Yet it adds complexity, so unless anyone + * files a bug explaining why they really need this feature, I'm going to + * remove it. Even if they do, the feature may need an update. + * + * The slice runs from the range's starting column to the range's end + * column, exclusive. It looks just like a normal result row, except + * slices can be empty. + */ + [[deprecated("Row slicing is going away. File a bug if you need it.")]] row + slice(size_type sbegin, size_type send) const; + + /// Is this a row without fields? Can only happen to a slice. + [[nodiscard, deprecated("Row slicing is going away.")]] PQXX_PURE bool + empty() const noexcept; + + /// Extract entire row's values into a tuple. + /** Converts to the types of the tuple's respective fields. + */ + template void to(Tuple &t) const + { + check_size(std::tuple_size_v); + convert(t); + } + + template std::tuple as() const + { + check_size(sizeof...(TYPE)); + using seq = std::make_index_sequence; + return get_tuple>(seq{}); + } + +protected: + friend class const_row_iterator; + friend class result; + row(result const &r, result_size_type index, size_type cols) noexcept; + + /// Throw @ref usage_error if row size is not `expected`. + void check_size(size_type expected) const + { + if (size() != expected) + throw usage_error{internal::concat( + "Tried to extract ", expected, " field(s) from a row of ", size(), + ".")}; + } + + /// Convert to a given tuple of values, don't check sizes. + /** We need this for cases where we have a full tuple of field types, but + * not a parameter pack. + */ + template TUPLE as_tuple() const + { + using seq = std::make_index_sequence>; + return get_tuple(seq{}); + } + + template friend class pqxx::internal::result_iter; + /// Convert entire row to tuple fields, without checking row size. + template void convert(Tuple &t) const + { + extract_fields(t, std::make_index_sequence>{}); + } + + friend class field; + + /// Result set of which this is one row. + result m_result; + + /// Row number. + /** + * You'd expect this to be unsigned, but due to the way reverse iterators + * are related to regular iterators, it must be allowed to underflow to -1. + */ + result::size_type m_index = 0; + + // TODO: Remove m_begin and (if possible) m_end when we remove slice(). + /// First column in slice. This row ignores lower-numbered columns. + size_type m_begin = 0; + /// End column in slice. This row only sees lower-numbered columns. + size_type m_end = 0; + +private: + template + void extract_fields(Tuple &t, std::index_sequence) const + { + (extract_value(t), ...); + } + + template + void extract_value(Tuple &t) const; + + /// Convert row's values as a new tuple. + template + auto get_tuple(std::index_sequence) const + { + return std::make_tuple(get_field()...); + } + + /// Extract and convert a field. + template auto get_field() const + { + return (*this)[index].as>(); + } +}; + + +/// Iterator for fields in a row. Use as row::const_iterator. +class PQXX_LIBEXPORT const_row_iterator : public field +{ +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = field const; + using pointer = field const *; + using size_type = row_size_type; + using difference_type = row_difference_type; + using reference = field; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + const_row_iterator() = default; +#include "pqxx/internal/ignore-deprecated-post.hxx" + const_row_iterator(row const &t, row_size_type c) noexcept : + field{t.m_result, t.m_index, c} + {} + const_row_iterator(field const &F) noexcept : field{F} {} + const_row_iterator(const_row_iterator const &) noexcept = default; + const_row_iterator(const_row_iterator &&) noexcept = default; + + /** + * @name Dereferencing operators + */ + //@{ + [[nodiscard]] constexpr pointer operator->() const noexcept { return this; } + [[nodiscard]] reference operator*() const noexcept { return {*this}; } + //@} + + /** + * @name Manipulations + */ + //@{ + const_row_iterator &operator=(const_row_iterator const &) noexcept = default; + const_row_iterator &operator=(const_row_iterator &&) noexcept = default; + + // TODO: noexcept. Breaks ABI. + const_row_iterator operator++(int); + const_row_iterator &operator++() noexcept + { + ++m_col; + return *this; + } + // TODO: noexcept. Breaks ABI. + const_row_iterator operator--(int); + const_row_iterator &operator--() noexcept + { + --m_col; + return *this; + } + + const_row_iterator &operator+=(difference_type i) noexcept + { + m_col = size_type(difference_type(m_col) + i); + return *this; + } + const_row_iterator &operator-=(difference_type i) noexcept + { + m_col = size_type(difference_type(m_col) - i); + return *this; + } + //@} + + /** + * @name Comparisons + */ + //@{ + [[nodiscard]] constexpr bool + operator==(const_row_iterator const &i) const noexcept + { + return col() == i.col(); + } + [[nodiscard]] constexpr bool + operator!=(const_row_iterator const &i) const noexcept + { + return col() != i.col(); + } + [[nodiscard]] constexpr bool + operator<(const_row_iterator const &i) const noexcept + { + return col() < i.col(); + } + [[nodiscard]] constexpr bool + operator<=(const_row_iterator const &i) const noexcept + { + return col() <= i.col(); + } + [[nodiscard]] constexpr bool + operator>(const_row_iterator const &i) const noexcept + { + return col() > i.col(); + } + [[nodiscard]] constexpr bool + operator>=(const_row_iterator const &i) const noexcept + { + return col() >= i.col(); + } + //@} + + /** + * @name Arithmetic operators + */ + //@{ + [[nodiscard]] inline const_row_iterator + operator+(difference_type) const noexcept; + + friend const_row_iterator + operator+(difference_type, const_row_iterator const &) noexcept; + + [[nodiscard]] inline const_row_iterator + operator-(difference_type) const noexcept; + [[nodiscard]] inline difference_type + operator-(const_row_iterator const &) const noexcept; + //@} +}; + + +/// Reverse iterator for a row. Use as row::const_reverse_iterator. +class PQXX_LIBEXPORT const_reverse_row_iterator : private const_row_iterator +{ +public: + using super = const_row_iterator; + using iterator_type = const_row_iterator; + using iterator_type::difference_type; + using iterator_type::iterator_category; + using iterator_type::pointer; + using value_type = iterator_type::value_type; + using reference = iterator_type::reference; + + const_reverse_row_iterator() noexcept = default; + const_reverse_row_iterator(const_reverse_row_iterator const &) noexcept = + default; + const_reverse_row_iterator(const_reverse_row_iterator &&) noexcept = default; + + explicit const_reverse_row_iterator(super const &rhs) noexcept : + const_row_iterator{rhs} + { + super::operator--(); + } + + [[nodiscard]] PQXX_PURE iterator_type base() const noexcept; + + /** + * @name Dereferencing operators + */ + //@{ + using iterator_type::operator->; + using iterator_type::operator*; + //@} + + /** + * @name Manipulations + */ + //@{ + const_reverse_row_iterator & + operator=(const_reverse_row_iterator const &r) noexcept + { + iterator_type::operator=(r); + return *this; + } + const_reverse_row_iterator operator++() noexcept + { + iterator_type::operator--(); + return *this; + } + // TODO: noexcept. Breaks ABI. + const_reverse_row_iterator operator++(int); + const_reverse_row_iterator &operator--() noexcept + { + iterator_type::operator++(); + return *this; + } + const_reverse_row_iterator operator--(int); + // TODO: noexcept. Breaks ABI. + const_reverse_row_iterator &operator+=(difference_type i) noexcept + { + iterator_type::operator-=(i); + return *this; + } + const_reverse_row_iterator &operator-=(difference_type i) noexcept + { + iterator_type::operator+=(i); + return *this; + } + //@} + + /** + * @name Arithmetic operators + */ + //@{ + [[nodiscard]] const_reverse_row_iterator + operator+(difference_type i) const noexcept + { + return const_reverse_row_iterator{base() - i}; + } + [[nodiscard]] const_reverse_row_iterator + operator-(difference_type i) noexcept + { + return const_reverse_row_iterator{base() + i}; + } + [[nodiscard]] difference_type + operator-(const_reverse_row_iterator const &rhs) const noexcept + { + return rhs.const_row_iterator::operator-(*this); + } + //@} + + /** + * @name Comparisons + */ + //@{ + [[nodiscard]] bool + operator==(const_reverse_row_iterator const &rhs) const noexcept + { + return iterator_type::operator==(rhs); + } + [[nodiscard]] bool + operator!=(const_reverse_row_iterator const &rhs) const noexcept + { + return !operator==(rhs); + } + + [[nodiscard]] constexpr bool + operator<(const_reverse_row_iterator const &rhs) const noexcept + { + return iterator_type::operator>(rhs); + } + [[nodiscard]] constexpr bool + operator<=(const_reverse_row_iterator const &rhs) const noexcept + { + return iterator_type::operator>=(rhs); + } + [[nodiscard]] constexpr bool + operator>(const_reverse_row_iterator const &rhs) const noexcept + { + return iterator_type::operator<(rhs); + } + [[nodiscard]] constexpr bool + operator>=(const_reverse_row_iterator const &rhs) const noexcept + { + return iterator_type::operator<=(rhs); + } + //@} +}; + + +const_row_iterator +const_row_iterator::operator+(difference_type o) const noexcept +{ + // TODO:: More direct route to home().columns()? + return { + row{home(), idx(), home().columns()}, + size_type(difference_type(col()) + o)}; +} + +inline const_row_iterator operator+( + const_row_iterator::difference_type o, const_row_iterator const &i) noexcept +{ + return i + o; +} + +inline const_row_iterator +const_row_iterator::operator-(difference_type o) const noexcept +{ + // TODO:: More direct route to home().columns()? + return { + row{home(), idx(), home().columns()}, + size_type(difference_type(col()) - o)}; +} + +inline const_row_iterator::difference_type +const_row_iterator::operator-(const_row_iterator const &i) const noexcept +{ + return difference_type(num() - i.num()); +} + + +template +inline void row::extract_value(Tuple &t) const +{ + using field_type = strip_t(t))>; + field const f{m_result, m_index, index}; + std::get(t) = from_string(f); +} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/separated_list b/ext/libpqxx-7.7.3/include/pqxx/separated_list new file mode 100644 index 000000000..1bdf51c6a --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/separated_list @@ -0,0 +1,6 @@ +/** Helper similar to Python's @c str.join(). + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/separated_list.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/separated_list.hxx b/ext/libpqxx-7.7.3/include/pqxx/separated_list.hxx new file mode 100644 index 000000000..d4230ea08 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/separated_list.hxx @@ -0,0 +1,142 @@ +/* Helper similar to Python's `str.join()`. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/separated_list instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_SEPARATED_LIST +#define PQXX_H_SEPARATED_LIST + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + +#include "pqxx/strconv.hxx" + +// C++20: Simplify using std::ranges::range. +// C++20: Optimise buffer allocation using random_access_range/iterator. +namespace pqxx +{ +/** + * @defgroup utility Utility functions + */ +//@{ + +/// Represent sequence of values as a string, joined by a given separator. +/** + * Use this to turn e.g. the numbers 1, 2, and 3 into a string "1, 2, 3". + * + * @param sep separator string (to be placed between items) + * @param begin beginning of items sequence + * @param end end of items sequence + * @param access functor defining how to dereference sequence elements + */ +template +[[nodiscard]] inline std::string +separated_list(std::string_view sep, ITER begin, ITER end, ACCESS access) +{ + if (end == begin) + return {}; + auto next{begin}; + ++next; + if (next == end) + return to_string(access(begin)); + + // From here on, we've got at least 2 elements -- meaning that we need sep. + using elt_type = strip_t; + using traits = string_traits; + + std::size_t budget{0}; + for (ITER cnt{begin}; cnt != end; ++cnt) + budget += traits::size_buffer(access(cnt)); + budget += + static_cast(std::distance(begin, end)) * std::size(sep); + + std::string result; + result.resize(budget); + + char *const data{result.data()}; + char *here{data}; + char *stop{data + budget}; + here = traits::into_buf(here, stop, access(begin)) - 1; + for (++begin; begin != end; ++begin) + { + here += sep.copy(here, std::size(sep)); + here = traits::into_buf(here, stop, access(begin)) - 1; + } + result.resize(static_cast(here - data)); + return result; +} + + +/// Render sequence as a string, using given separator between items. +template +[[nodiscard]] inline std::string +separated_list(std::string_view sep, ITER begin, ITER end) +{ + return separated_list(sep, begin, end, [](ITER i) { return *i; }); +} + + +/// Render items in a container as a string, using given separator. +template +[[nodiscard]] inline auto +separated_list(std::string_view sep, CONTAINER const &c) + /* + Always std::string; necessary because SFINAE doesn't work with the + contents of function bodies, so the check for iterability has to be in + the signature. + */ + -> typename std::enable_if< + (not std::is_void::value and + not std::is_void::value), + std::string>::type +{ + return separated_list(sep, std::begin(c), std::end(c)); +} + + +/// Render items in a tuple as a string, using given separator. +template< + typename TUPLE, std::size_t INDEX = 0, typename ACCESS, + typename std::enable_if< + (INDEX == std::tuple_size::value - 1), int>::type = 0> +[[nodiscard]] inline std::string separated_list( + std::string_view /* sep */, TUPLE const &t, ACCESS const &access) +{ + return to_string(access(&std::get(t))); +} + +template< + typename TUPLE, std::size_t INDEX = 0, typename ACCESS, + typename std::enable_if< + (INDEX < std::tuple_size::value - 1), int>::type = 0> +[[nodiscard]] inline std::string +separated_list(std::string_view sep, TUPLE const &t, ACCESS const &access) +{ + std::string out{to_string(access(&std::get(t)))}; + out.append(sep); + out.append(separated_list(sep, t, access)); + return out; +} + +template< + typename TUPLE, std::size_t INDEX = 0, + typename std::enable_if< + (INDEX <= std::tuple_size::value), int>::type = 0> +[[nodiscard]] inline std::string +separated_list(std::string_view sep, TUPLE const &t) +{ + // TODO: Optimise allocation. + return separated_list(sep, t, [](TUPLE const &tup) { return *tup; }); +} +//@} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/strconv b/ext/libpqxx-7.7.3/include/pqxx/strconv new file mode 100644 index 000000000..aa2c40ed5 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/strconv @@ -0,0 +1,6 @@ +/** String conversion definitions. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/strconv.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/strconv.hxx b/ext/libpqxx-7.7.3/include/pqxx/strconv.hxx new file mode 100644 index 000000000..863711228 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/strconv.hxx @@ -0,0 +1,468 @@ +/* String conversion definitions. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/stringconv instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_STRCONV +#define PQXX_H_STRCONV + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include +#include +#include +#include + +#if __has_include() +# include +#endif + +#if defined(PQXX_HAVE_RANGES) && __has_include() +# include +#endif + +#include "pqxx/except.hxx" +#include "pqxx/util.hxx" +#include "pqxx/zview.hxx" + + +namespace pqxx::internal +{ +/// Attempt to demangle @c std::type_info::name() to something human-readable. +PQXX_LIBEXPORT std::string demangle_type_name(char const[]); +} // namespace pqxx::internal + + +namespace pqxx +{ +/** + * @defgroup stringconversion String conversion + * + * The PostgreSQL server accepts and represents data in string form. It has + * its own formats for various data types. The string conversions define how + * various C++ types translate to and from their respective PostgreSQL text + * representations. + * + * Each conversion is defined by a specialisations of @c string_traits. It + * gets complicated if you want top performance, but until you do, all you + * really need to care about when converting values between C++ in-memory + * representations such as @c int and the postgres string representations is + * the @c pqxx::to_string and @c pqxx::from_string functions. + * + * If you need to convert a type which is not supported out of the box, you'll + * need to define your own specialisations for these templates, similar to the + * ones defined here and in `pqxx/conversions.hxx`. Any conversion code which + * "sees" your specialisation will now support your conversion. In particular, + * you'll be able to read result fields into a variable of the new type. + * + * There is a macro to help you define conversions for individual enumeration + * types. The conversion will represent enumeration values as numeric strings. + */ +//@{ + +/// A human-readable name for a type, used in error messages and such. +/** Actually this may not always be very user-friendly. It uses + * @c std::type_info::name(). On gcc-like compilers we try to demangle its + * output. Visual Studio produces human-friendly names out of the box. + * + * This variable is not inline. Inlining it gives rise to "memory leak" + * warnings from asan, the address sanitizer, possibly from use of + * @c std::type_info::name. + */ +template +std::string const type_name{internal::demangle_type_name(typeid(TYPE).name())}; + + +/// Traits describing a type's "null value," if any. +/** Some C++ types have a special value or state which correspond directly to + * SQL's NULL. + * + * The @c nullness traits describe whether it exists, and whether a particular + * value is null. + */ +template struct nullness +{ + /// Does this type have a null value? + static bool has_null; + + /// Is this type always null? + static bool always_null; + + /// Is @c value a null? + static bool is_null(TYPE const &value); + + /// Return a null value. + /** Don't use this in generic code to compare a value and see whether it is + * null. Some types may have multiple null values which do not compare as + * equal, or may define a null value which is not equal to anything including + * itself, like in SQL. + */ + [[nodiscard]] static TYPE null(); +}; + + +/// Nullness traits describing a type which does not have a null value. +template struct no_null +{ + /// Does @c TYPE have a "built-in null value"? + /** For example, a pointer can equal @c nullptr, which makes a very natural + * representation of an SQL null value. For such types, the code sometimes + * needs to make special allowances. + * + * for most types, such as @c int or @c std::string, there is no built-in + * null. If you want to represent an SQL null value for such a type, you + * would have to wrap it in something that does have a null value. For + * example, you could use @c std::optional for "either an @c int or a + * null value." + */ + static constexpr bool has_null = false; + + /// Are all values of this type null? + /** There are a few special C++ types which are always null - mainly + * @c std::nullptr_t. + */ + static constexpr bool always_null = false; + + /// Does a given value correspond to an SQL null value? + /** Most C++ types, such as @c int or @c std::string, have no inherent null + * value. But some types such as C-style string pointers do have a natural + * equivalent to an SQL null. + */ + [[nodiscard]] static constexpr bool is_null(TYPE const &) noexcept + { + return false; + } +}; + + +/// Traits class for use in string conversions. +/** Specialize this template for a type for which you wish to add to_string + * and from_string support. + * + * String conversions are not meant to work for nulls. Check for null before + * converting a value of @c TYPE to a string, or vice versa. + */ +template struct string_traits +{ + /// Return a @c string_view representing value, plus terminating zero. + /** Produces a @c string_view containing the PostgreSQL string representation + * for @c value. + * + * Uses the space from @c begin to @c end as a buffer, if needed. The + * returned string may lie somewhere in that buffer, or it may be a + * compile-time constant, or it may be null if value was a null value. Even + * if the string is stored in the buffer, its @c begin() may or may not be + * the same as @c begin. + * + * The @c string_view is guaranteed to be valid as long as the buffer from + * @c begin to @c end remains accessible and unmodified. + * + * @throws pqxx::conversion_overrun if the provided buffer space may not be + * enough. For maximum performance, this is a conservative estimate. It may + * complain about a buffer which is actually large enough for your value, if + * an exact check gets too expensive. + */ + [[nodiscard]] static inline zview + to_buf(char *begin, char *end, TYPE const &value); + + /// Write value's string representation into buffer at @c begin. + /** Assumes that value is non-null. + * + * Writes value's string representation into the buffer, starting exactly at + * @c begin, and ensuring a trailing zero. Returns the address just beyond + * the trailing zero, so the caller could use it as the @c begin for another + * call to @c into_buf writing a next value. + */ + static inline char *into_buf(char *begin, char *end, TYPE const &value); + + /// Parse a string representation of a @c TYPE value. + /** Throws @c conversion_error if @c value does not meet the expected format + * for a value of this type. + */ + [[nodiscard]] static inline TYPE from_string(std::string_view text); + + // C++20: Can we make these all constexpr? + /// Estimate how much buffer space is needed to represent value. + /** The estimate may be a little pessimistic, if it saves time. + * + * The estimate includes the terminating zero. + */ + [[nodiscard]] static inline std::size_t + size_buffer(TYPE const &value) noexcept; +}; + + +/// Nullness: Enums do not have an inherent null value. +template +struct nullness>> : no_null +{}; +} // namespace pqxx + + +namespace pqxx::internal +{ +/// Helper class for defining enum conversions. +/** The conversion will convert enum values to numeric strings, and vice versa. + * + * To define a string conversion for an enum type, derive a @c string_traits + * specialisation for the enum from this struct. + * + * There's usually an easier way though: the @c PQXX_DECLARE_ENUM_CONVERSION + * macro. Use @c enum_traits manually only if you need to customise your + * traits type in more detail. + */ +template struct enum_traits +{ + using impl_type = std::underlying_type_t; + using impl_traits = string_traits; + + [[nodiscard]] static constexpr zview + to_buf(char *begin, char *end, ENUM const &value) + { + return impl_traits::to_buf(begin, end, to_underlying(value)); + } + + static constexpr char *into_buf(char *begin, char *end, ENUM const &value) + { + return impl_traits::into_buf(begin, end, to_underlying(value)); + } + + [[nodiscard]] static ENUM from_string(std::string_view text) + { + return static_cast(impl_traits::from_string(text)); + } + + [[nodiscard]] static std::size_t size_buffer(ENUM const &value) noexcept + { + return impl_traits::size_buffer(to_underlying(value)); + } + +private: + // C++23: Replace with std::to_underlying. + static constexpr impl_type to_underlying(ENUM const &value) noexcept + { + return static_cast(value); + } +}; +} // namespace pqxx::internal + + +/// Macro: Define a string conversion for an enum type. +/** This specialises the @c pqxx::string_traits template, so use it in the + * @c ::pqxx namespace. + * + * For example: + * + * #include + * #include + * enum X { xa, xb }; + * namespace pqxx { PQXX_DECLARE_ENUM_CONVERSION(x); } + * int main() { std::cout << pqxx::to_string(xa) << std::endl; } + */ +#define PQXX_DECLARE_ENUM_CONVERSION(ENUM) \ + template<> struct string_traits : pqxx::internal::enum_traits \ + {}; \ + template<> inline std::string const type_name { #ENUM } + + +namespace pqxx +{ +/// Parse a value in postgres' text format as a TYPE. +/** If the form of the value found in the string does not match the expected + * type, e.g. if a decimal point is found when converting to an integer type, + * the conversion fails. Overflows (e.g. converting "9999999999" to a 16-bit + * C++ type) are also treated as errors. If in some cases this behaviour + * should be inappropriate, convert to something bigger such as @c long @c int + * first and then truncate the resulting value. + * + * Only the simplest possible conversions are supported. Fancy features like + * hexadecimal or octal, spurious signs, or exponent notation won't work. + * Whitespace is not stripped away. Only the kinds of strings that come out of + * PostgreSQL and out of to_string() can be converted. + */ +template +[[nodiscard]] inline TYPE from_string(std::string_view text) +{ + return string_traits::from_string(text); +} + + +/// "Convert" a std::string_view to a std::string_view. +/** Just returns its input. + * + * @warning Of course the result is only valid for as long as the original + * string remains valid! Never access the string referenced by the return + * value after the original has been destroyed. + */ +template<> +[[nodiscard]] inline std::string_view from_string(std::string_view text) +{ + return text; +} + + +/// Attempt to convert postgres-generated string to given built-in object. +/** This is like the single-argument form of the function, except instead of + * returning the value, it sets @c value. + * + * You may find this more convenient in that it infers the type you want from + * the argument you pass. But there are disadvantages: it requires an + * assignment operator, and it may be less efficient. + */ +template inline void from_string(std::string_view text, T &value) +{ + value = from_string(text); +} + + +/// Convert a value to a readable string that PostgreSQL will understand. +/** The conversion does no special formatting, and ignores any locale settings. + * The resulting string will be human-readable and in a format suitable for use + * in SQL queries. It won't have niceties such as "thousands separators" + * though. + */ +template inline std::string to_string(TYPE const &value); + + +/// Convert multiple values to strings inside a single buffer. +/** There must be enough room for all values, or this will throw + * @c conversion_overrun. You can obtain a conservative estimate of the buffer + * space required by calling @c size_buffer() on the values. + * + * The @c std::string_view results may point into the buffer, so don't assume + * that they will remain valid after you destruct or move the buffer. + */ +template +[[nodiscard]] inline std::vector +to_buf(char *here, char const *end, TYPE... value) +{ + return {[&here, end](auto v) { + auto begin = here; + here = string_traits::into_buf(begin, end, v); + // Exclude the trailing zero out of the string_view. + auto len{static_cast(here - begin) - 1}; + return std::string_view{begin, len}; + }(value)...}; +} + +/// Convert a value to a readable string that PostgreSQL will understand. +/** This variant of to_string can sometimes save a bit of time in loops, by + * re-using a std::string for multiple conversions. + */ +template +inline void into_string(TYPE const &value, std::string &out); + + +/// Is @c value null? +template +[[nodiscard]] inline constexpr bool is_null(TYPE const &value) noexcept +{ + return nullness>::is_null(value); +} + + +/// Estimate how much buffer space is needed to represent values as a string. +/** The estimate may be a little pessimistic, if it saves time. It also + * includes room for a terminating zero after each value. + */ +template +[[nodiscard]] inline std::size_t size_buffer(TYPE const &...value) noexcept +{ + return (string_traits>::size_buffer(value) + ...); +} + + +/// Does this type translate to an SQL array? +/** Specialisations may override this to be true for container types. + * + * This may not always be a black-and-white choice. For instance, a + * @c std::string is a container, but normally it translates to an SQL string, + * not an SQL array. + */ +template inline constexpr bool is_sql_array{false}; + + +/// Can we use this type in arrays and composite types without quoting them? +/** Define this as @c true only if values of @c TYPE can never contain any + * special characters that might need escaping or confuse the parsing of array + * or composite * types, such as commas, quotes, parentheses, braces, newlines, + * and so on. + * + * When converting a value of such a type to a string in an array or a field in + * a composite type, we do not need to add quotes, nor escape any special + * characters. + * + * This is just an optimisation, so it defaults to @c false to err on the side + * of slow correctness. + */ +template inline constexpr bool is_unquoted_safe{false}; + + +/// Element separator between SQL array elements of this type. +template inline constexpr char array_separator{','}; + + +/// What's the preferred format for passing non-null parameters of this type? +/** This affects how we pass parameters of @c TYPE when calling parameterised + * statements or prepared statements. + * + * Generally we pass parameters in text format, but binary strings are the + * exception. We also pass nulls in binary format, so this function need not + * handle null values. + */ +template inline constexpr format param_format(TYPE const &) +{ + return format::text; +} + + +/// Implement @c string_traits::to_buf by calling @c into_buf. +/** When you specialise @c string_traits for a new type, most of the time its + * @c to_buf implementation has no special optimisation tricks and just writes + * its text into the buffer it receives from the caller, starting at the + * beginning. + * + * In that common situation, you can implement @c to_buf as just a call to + * @c generic_to_buf. It will call @c into_buf and return the right result for + * @c to_buf. + */ +template +inline zview generic_to_buf(char *begin, char *end, TYPE const &value) +{ + using traits = string_traits; + // The trailing zero does not count towards the zview's size, so subtract 1 + // from the result we get from into_buf(). + if (is_null(value)) + return {}; + else + return {begin, traits::into_buf(begin, end, value) - begin - 1}; +} + + +#if defined(PQXX_HAVE_CONCEPTS) +/// Concept: Binary string, akin to @c std::string for binary data. +/** Any type that satisfies this concept can represent an SQL BYTEA value. + * + * A @c binary has a @c begin(), @c end(), @c size(), and @data(). Each byte + * is a @c std::byte, and they must all be laid out contiguously in memory so + * we can reference them by a pointer. + */ +template +concept binary = std::ranges::contiguous_range and + std::is_same_v>, std::byte>; +#endif +//@} +} // namespace pqxx + + +#include "pqxx/internal/conversions.hxx" +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/stream_from b/ext/libpqxx-7.7.3/include/pqxx/stream_from new file mode 100644 index 000000000..972762443 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/stream_from @@ -0,0 +1,8 @@ +/** pqxx::stream_from class. + * + * pqxx::stream_from enables optimized batch reads from a database table. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/stream_from.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/stream_from.hxx b/ext/libpqxx-7.7.3/include/pqxx/stream_from.hxx new file mode 100644 index 000000000..ff4a93d2e --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/stream_from.hxx @@ -0,0 +1,361 @@ +/* Definition of the pqxx::stream_from class. + * + * pqxx::stream_from enables optimized batch reads from a database table. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/stream_from instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_STREAM_FROM +#define PQXX_H_STREAM_FROM + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include + +#include "pqxx/connection.hxx" +#include "pqxx/except.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/encoding_group.hxx" +#include "pqxx/internal/stream_iterator.hxx" +#include "pqxx/separated_list.hxx" +#include "pqxx/transaction_focus.hxx" + + +namespace pqxx +{ +class transaction_base; + + +/// Pass this to a `stream_from` constructor to stream table contents. +/** @deprecated Use @ref stream_from::table() instead. + */ +constexpr from_table_t from_table; +/// Pass this to a `stream_from` constructor to stream query results. +/** @deprecated Use stream_from::query() instead. + */ +constexpr from_query_t from_query; + + +/// Stream data from the database. +/** For larger data sets, retrieving data this way is likely to be faster than + * executing a query and then iterating and converting the rows fields. You + * will also be able to start processing before all of the data has come in. + * + * There are also downsides. Not all kinds of query will work in a stream. + * But straightforward `SELECT` and `UPDATE ... RETURNING` queries should work. + * This function makes use of @ref pqxx::stream_from, which in turn uses + * PostgreSQL's `COPY` command, so see the documentation for those to get the + * full details. + * + * There are other downsides. If there stream encounters an error, it may + * leave the entire connection in an unusable state, so you'll have to give the + * whole thing up. Finally, opening a stream puts the connection in a special + * state, so you won't be able to do many other things with the connection or + * the transaction while the stream is open. + * + * There are two ways of starting a stream: you stream either all rows in a + * table (using one of the factories, `table()` or `raw_table()`), or the + * results of a query (using the `query()` factory). + * + * Usually you'll want the `stream` convenience wrapper in + * @ref transaction_base, * so you don't need to deal with this class directly. + * + * @warning While a stream is active, you cannot execute queries, open a + * pipeline, etc. on the same transaction. A transaction can have at most one + * object of a type derived from @ref pqxx::transaction_focus active on it at a + * time. + */ +class PQXX_LIBEXPORT stream_from : transaction_focus +{ +public: + using raw_line = + std::pair>, std::size_t>; + + /// Factory: Execute query, and stream the results. + /** The query can be a SELECT query or a VALUES query; or it can be an + * UPDATE, INSERT, or DELETE with a RETURNING clause. + * + * The query is executed as part of a COPY statement, so there are additional + * restrictions on what kind of query you can use here. See the PostgreSQL + * documentation for the COPY command: + * + * https://www.postgresql.org/docs/current/sql-copy.html + */ + static stream_from query(transaction_base &tx, std::string_view q) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return {tx, from_query, q}; +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /** + * @name Streaming data from tables + * + * You can use `stream_from` to read a table's contents. This is a quick + * and easy way to read a table, but it comes with limitations. It cannot + * stream from a view, only from a table. It does not support conditions. + * And there are no guarantees about ordering. If you need any of those + * things, consider streaming from a query instead. + */ + //@{ + + /// Factory: Stream data from a pre-quoted table and columns. + /** Use this factory if you need to create multiple streams using the same + * table path and/or columns list, and you want to save a bit of work on + * composing the internal SQL statement for starting the stream. It lets you + * compose the string representations for the table path and the columns + * list, so you can compute these once and then re-use them later. + * + * @param tx The transaction within which the stream will operate. + * @param path Name or path for the table upon which the stream will + * operate. If any part of the table path may contain special + * characters or be case-sensitive, quote the path using + * pqxx::connection::quote_table(). + * @param columns Columns which the stream will read. They should be + * comma-separated and, if needed, quoted. You can produce the string + * using pqxx::connection::quote_columns(). If you omit this argument, + * the stream will read all columns in the table, in schema order. + */ + static stream_from raw_table( + transaction_base &tx, std::string_view path, + std::string_view columns = ""sv); + + /// Factory: Stream data from a given table. + /** This is the convenient way to stream from a table. + */ + static stream_from table( + transaction_base &tx, table_path path, + std::initializer_list columns = {}); + //@} + + /// Execute query, and stream over the results. + /** @deprecated Use factory function @ref query instead. + */ + [[deprecated("Use query() factory instead.")]] stream_from( + transaction_base &, from_query_t, std::string_view query); + + /// Stream all rows in table, all columns. + /** @deprecated Use factories @ref table or @ref raw_table instead. + */ + [[deprecated("Use table() or raw_table() factory instead.")]] stream_from( + transaction_base &, from_table_t, std::string_view table); + + /// Stream given columns from all rows in table. + /** @deprecated Use factories @ref table or @ref raw_table instead. + */ + template + [[deprecated("Use table() or raw_table() factory instead.")]] stream_from( + transaction_base &, from_table_t, std::string_view table, + Iter columns_begin, Iter columns_end); + + /// Stream given columns from all rows in table. + /** @deprecated Use factory function @ref query instead. + */ + template + [[deprecated("Use table() or raw_table() factory instead.")]] stream_from( + transaction_base &tx, from_table_t, std::string_view table, + Columns const &columns); + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + /// @deprecated Use factories @ref table or @ref raw_table instead. + [[deprecated("Use the from_table_t overload instead.")]] stream_from( + transaction_base &tx, std::string_view table) : + stream_from{tx, from_table, table} + {} +#include "pqxx/internal/ignore-deprecated-post.hxx" + + /// @deprecated Use factories @ref table or @ref raw_table instead. + template + [[deprecated("Use the from_table_t overload instead.")]] stream_from( + transaction_base &tx, std::string_view table, Columns const &columns) : + stream_from{tx, from_table, table, columns} + {} + + /// @deprecated Use factories @ref table or @ref raw_table instead. + template + [[deprecated("Use the from_table_t overload instead.")]] stream_from( + transaction_base &, std::string_view table, Iter columns_begin, + Iter columns_end); + + ~stream_from() noexcept; + + /// May this stream still produce more data? + [[nodiscard]] constexpr operator bool() const noexcept + { + return not m_finished; + } + /// Has this stream produced all the data it is going to produce? + [[nodiscard]] constexpr bool operator!() const noexcept + { + return m_finished; + } + + /// Finish this stream. Call this before continuing to use the connection. + /** Consumes all remaining lines, and closes the stream. + * + * This may take a while if you're abandoning the stream before it's done, so + * skip it in error scenarios where you're not planning to use the connection + * again afterwards. + */ + void complete(); + + /// Read one row into a tuple. + /** Converts the row's fields into the fields making up the tuple. + * + * For a column which can contain nulls, be sure to give the corresponding + * tuple field a type which can be null. For example, to read a field as + * `int` when it may contain nulls, read it as `std::optional`. + * Using `std::shared_ptr` or `std::unique_ptr` will also work. + */ + template stream_from &operator>>(Tuple &); + + /// Doing this with a `std::variant` is going to be horrifically borked. + template + stream_from &operator>>(std::variant &) = delete; + + /// Iterate over this stream. Supports range-based "for" loops. + /** Produces an input iterator over the stream. + * + * Do not call this yourself. Use it like "for (auto data : stream.iter())". + */ + template [[nodiscard]] auto iter() & + { + return pqxx::internal::stream_input_iteration{*this}; + } + + /// Read a row. Return fields as views, valid until you read the next row. + /** Returns `nullptr` when there are no more rows to read. Do not attempt + * to read any further rows after that. + * + * Do not access the vector, or the storage referenced by the views, after + * closing or completing the stream, or after attempting to read a next row. + * + * A @ref pqxx::zview is like a `std::string_view`, but with the added + * guarantee that if its data pointer is non-null, the string is followed by + * a terminating zero (which falls just outside the view itself). + * + * If any of the views' data pointer is null, that means that the + * corresponding SQL field is null. + * + * @warning The return type may change in the future, to support C++20 + * coroutine-based usage. + */ + std::vector const *read_row() &; + + /// Read a raw line of text from the COPY command. + /** @warning Do not use this unless you really know what you're doing. */ + raw_line get_raw_line(); + +private: + // TODO: Clean up this signature once we cull the deprecated constructors. + /// @deprecated + stream_from( + transaction_base &tx, std::string_view table, std::string_view columns, + from_table_t); + + // TODO: Clean up this signature once we cull the deprecated constructors. + /// @deprecated + stream_from( + transaction_base &, std::string_view unquoted_table, + std::string_view columns, from_table_t, int); + + template + void extract_fields(Tuple &t, std::index_sequence) const + { + (extract_value(t), ...); + } + + pqxx::internal::glyph_scanner_func *m_glyph_scanner; + + /// Current row's fields' text, combined into one reusable string. + std::string m_row; + + /// The current row's fields. + std::vector m_fields; + + bool m_finished = false; + + void close(); + + template + void extract_value(Tuple &) const; + + /// Read a line of COPY data, write `m_row` and `m_fields`. + void parse_line(); +}; + + +template +inline stream_from::stream_from( + transaction_base &tx, from_table_t, std::string_view table_name, + Columns const &columns) : + stream_from{ + tx, from_table, table_name, std::begin(columns), std::end(columns)} +{} + + +template +inline stream_from::stream_from( + transaction_base &tx, from_table_t, std::string_view table, + Iter columns_begin, Iter columns_end) : + stream_from{ + tx, table, separated_list(",", columns_begin, columns_end), + from_table, 1} +{} + + +template inline stream_from &stream_from::operator>>(Tuple &t) +{ + if (m_finished) + return *this; + static constexpr auto tup_size{std::tuple_size_v}; + m_fields.reserve(tup_size); + parse_line(); + if (m_finished) + return *this; + + if (std::size(m_fields) != tup_size) + throw usage_error{internal::concat( + "Tried to extract ", tup_size, " field(s) from a stream of ", + std::size(m_fields), ".")}; + + extract_fields(t, std::make_index_sequence{}); + return *this; +} + + +template +inline void stream_from::extract_value(Tuple &t) const +{ + using field_type = strip_t(t))>; + using nullity = nullness; + assert(index < std::size(m_fields)); + if constexpr (nullity::always_null) + { + if (std::data(m_fields[index]) != nullptr) + throw conversion_error{"Streaming non-null value into null field."}; + } + else if (std::data(m_fields[index]) == nullptr) + { + if constexpr (nullity::has_null) + std::get(t) = nullity::null(); + else + internal::throw_null_conversion(type_name); + } + else + { + // Don't ever try to convert a non-null value to nullptr_t! + std::get(t) = from_string(m_fields[index]); + } +} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/stream_to b/ext/libpqxx-7.7.3/include/pqxx/stream_to new file mode 100644 index 000000000..8760cf1f4 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/stream_to @@ -0,0 +1,8 @@ +/** pqxx::stream_to class. + * + * pqxx::stream_to enables optimized batch updates to a database table. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/stream_to.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/stream_to.hxx b/ext/libpqxx-7.7.3/include/pqxx/stream_to.hxx new file mode 100644 index 000000000..2a49d8f85 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/stream_to.hxx @@ -0,0 +1,455 @@ +/* Definition of the pqxx::stream_to class. + * + * pqxx::stream_to enables optimized batch updates to a database table. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/stream_to.hxx instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_STREAM_TO +#define PQXX_H_STREAM_TO + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/separated_list.hxx" +#include "pqxx/transaction_base.hxx" + + +namespace pqxx +{ +/// Efficiently write data directly to a database table. +/** If you wish to insert rows of data into a table, you can compose INSERT + * statements and execute them. But it's slow and tedious, and you need to + * worry about quoting and escaping the data. + * + * If you're just inserting a single row, it probably won't matter much. You + * can use prepared or parameterised statements to take care of the escaping + * for you. But if you're inserting large numbers of rows you will want + * something better. + * + * Inserting rows one by one using INSERT statements involves a lot of + * pointless overhead, especially when you are working with a remote database + * server over the network. You may end up sending each row over the network + * as a separate query, and waiting for a reply. Do it "in bulk" using + * `stream_to`, and you may find that it goes many times faster. Sometimes + * you gain orders of magnitude in speed. + * + * Here's how it works: you create a `stream_to` stream to start writing to + * your table. You will probably want to specify the columns. Then, you + * feed your data into the stream one row at a time. And finally, you call the + * stream's @ref complete function to tell it to finalise the operation, wait + * for completion, and check for errors. + * + * (You _must_ complete the stream before committing or aborting the + * transaction. The connection is in a special state while the stream is + * active, where it can't process commands, and can't commit or abort a + * transaction.) + * + * So how do you feed a row of data into the stream? There's several ways, but + * the preferred one is to call its @ref write_values. Pass the field values + * as arguments. Doesn't matter what type they are, as long as libpqxx knows + * how to convert them to PostgreSQL's text format: `int`, `std::string` or + * `std:string_view`, `float` and `double`, `bool`... lots of basic types + * are supported. If some of the values are null, feel free to use + * `std::optional`, `std::shared_ptr`, or `std::unique_ptr`. + * + * The arguments' types don't even have to match the fields' SQL types. If you + * want to insert an `int` into a `DECIMAL` column, that's your choice -- it + * will produce a `DECIMAL` value which happens to be integral. Insert a + * `float` into a `VARCHAR` column? That's fine, you'll get a string whose + * contents happen to read like a number. And so on. You can even insert + * different types of value in the same column on different rows. If you have + * a code path where a particular field is always null, just insert `nullptr`. + * + * There is another way to insert rows: the `<<` ("shift-left") operator. + * It's not as fast and it doesn't support variable arguments: each row must be + * either a `std::tuple` or something iterable, such as a `std::vector`, or + * anything else with a `begin()` and `end()`. + * + * @warning While a stream is active, you cannot execute queries, open a + * pipeline, etc. on the same transaction. A transaction can have at most one + * object of a type derived from @ref pqxx::transaction_focus active on it at a + * time. + */ +class PQXX_LIBEXPORT stream_to : transaction_focus +{ +public: + /// Stream data to a pre-quoted table and columns. + /** This factory can be useful when it's not convenient to provide the + * columns list in the form of a `std::initializer_list`, or when the list + * of columns is simply not known at compile time. + * + * Also use this if you need to create multiple streams using the same table + * path and/or columns list, and you want to save a bit of work on composing + * the internal SQL statement for starting the stream. It lets you compose + * the string representations for the table path and the columns list, so you + * can compute these once and then re-use them later. + * + * @param tx The transaction within which the stream will operate. + * @param path Name or path for the table upon which the stream will + * operate. If any part of the table path may contain special + * characters or be case-sensitive, quote the path using + * pqxx::connection::quote_table(). + * @param columns Columns to which the stream will write. They should be + * comma-separated and, if needed, quoted. You can produce the string + * using pqxx::connection::quote_columns(). If you omit this argument, + * the stream will write all columns in the table, in schema order. + */ + static stream_to raw_table( + transaction_base &tx, std::string_view path, std::string_view columns = "") + { + return {tx, path, columns}; + } + + /// Create a `stream_to` writing to a named table and columns. + /** Use this to stream data to a table, where the list of columns is known at + * compile time. + * + * @param tx The transaction within which the stream will operate. + * @param path A @ref table_path designating the target table. + * @param columns Optionally, the columns to which the stream should write. + * If you do not pass this, the stream will write to all columns in the + * table, in schema order. + */ + static stream_to table( + transaction_base &tx, table_path path, + std::initializer_list columns = {}) + { + auto const &conn{tx.conn()}; + return raw_table(tx, conn.quote_table(path), conn.quote_columns(columns)); + } + +#if defined(PQXX_HAVE_CONCEPTS) + /// Create a `stream_to` writing to a named table and columns. + /** Use this version to stream data to a table, when the list of columns is + * not known at compile time. + * + * @param tx The transaction within which the stream will operate. + * @param path A @ref table_path designating the target table. + * @param columns The columns to which the stream should write. + */ + template + static stream_to + table(transaction_base &tx, table_path path, COLUMNS const &columns) + { + auto const &conn{tx.conn()}; + return stream_to::raw_table( + tx, conn.quote_table(path), tx.conn().quote_columns(columns)); + } + + /// Create a `stream_to` writing to a named table and columns. + /** Use this version to stream data to a table, when the list of columns is + * not known at compile time. + * + * @param tx The transaction within which the stream will operate. + * @param path A @ref table_path designating the target table. + * @param columns The columns to which the stream should write. + */ + template + static stream_to + table(transaction_base &tx, std::string_view path, COLUMNS const &columns) + { + return stream_to::raw_table(tx, path, tx.conn().quote_columns(columns)); + } +#endif // PQXX_HAVE_CONCEPTS + + /// Create a stream, without specifying columns. + /** @deprecated Use @ref table or @ref raw_table as a factory. + * + * Fields will be inserted in whatever order the columns have in the + * database. + * + * You'll probably want to specify the columns, so that the mapping between + * your data fields and the table is explicit in your code, and not hidden + * in an "implicit contract" between your code and your schema. + */ + [[deprecated("Use table() or raw_table() factory.")]] stream_to( + transaction_base &tx, std::string_view table_name) : + stream_to{tx, table_name, ""sv} + {} + + /// Create a stream, specifying column names as a container of strings. + /** @deprecated Use @ref table or @ref raw_table as a factory. + */ + template + [[deprecated("Use table() or raw_table() factory.")]] stream_to( + transaction_base &, std::string_view table_name, Columns const &columns); + + /// Create a stream, specifying column names as a sequence of strings. + /** @deprecated Use @ref table or @ref raw_table as a factory. + */ + template + [[deprecated("Use table() or raw_table() factory.")]] stream_to( + transaction_base &, std::string_view table_name, Iter columns_begin, + Iter columns_end); + + ~stream_to() noexcept; + + /// Does this stream still need to @ref complete()? + [[nodiscard]] constexpr operator bool() const noexcept + { + return not m_finished; + } + /// Has this stream been through its concluding @c complete()? + [[nodiscard]] constexpr bool operator!() const noexcept + { + return m_finished; + } + + /// Complete the operation, and check for errors. + /** Always call this to close the stream in an orderly fashion, even after + * an error. (In the case of an error, abort the transaction afterwards.) + * + * The only circumstance where it's safe to skip this is after an error, if + * you're discarding the entire connection. + */ + void complete(); + + /// Insert a row of data. + /** Returns a reference to the stream, so you can chain the calls. + * + * The @c row can be a tuple, or any type that can be iterated. Each + * item becomes a field in the row, in the same order as the columns you + * specified when creating the stream. + * + * If you don't already happen to have your fields in the form of a tuple or + * container, prefer @c write_values. It's faster and more convenient. + */ + template stream_to &operator<<(Row const &row) + { + write_row(row); + return *this; + } + + /// Stream a `stream_from` straight into a `stream_to`. + /** This can be useful when copying between different databases. If the + * source and the destination are on the same database, you'll get better + * performance doing it all in a regular query. + */ + stream_to &operator<<(stream_from &); + + /// Insert a row of data, given in the form of a @c std::tuple or container. + /** The @c row can be a tuple, or any type that can be iterated. Each + * item becomes a field in the row, in the same order as the columns you + * specified when creating the stream. + * + * The preferred way to insert a row is @c write_values. + */ + template void write_row(Row const &row) + { + fill_buffer(row); + write_buffer(); + } + + /// Insert values as a row. + /** This is the recommended way of inserting data. Pass your field values, + * of any convertible type. + */ + template void write_values(Ts const &...fields) + { + fill_buffer(fields...); + write_buffer(); + } + +private: + /// Stream a pre-quoted table name and columns list. + stream_to( + transaction_base &tx, std::string_view path, std::string_view columns); + + bool m_finished = false; + + /// Reusable buffer for a row. Saves doing an allocation for each row. + std::string m_buffer; + + /// Reusable buffer for converting/escaping a field. + std::string m_field_buf; + + /// Glyph scanner, for parsing the client encoding. + internal::glyph_scanner_func *m_scanner; + + /// Write a row of raw text-format data into the destination table. + void write_raw_line(std::string_view); + + /// Write a row of data from @c m_buffer into the destination table. + /** Resets the buffer for the next row. + */ + void write_buffer(); + + /// COPY encoding for a null field, plus subsequent separator. + static constexpr std::string_view null_field{"\\N\t"}; + + /// Estimate buffer space needed for a field which is always null. + template + static std::enable_if_t::always_null, std::size_t> + estimate_buffer(T const &) + { + return std::size(null_field); + } + + /// Estimate buffer space needed for field f. + /** The estimate is not very precise. We don't actually know how much space + * we'll need once the escaping comes in. + */ + template + static std::enable_if_t::always_null, std::size_t> + estimate_buffer(T const &field) + { + return is_null(field) ? std::size(null_field) : size_buffer(field); + } + + /// Append escaped version of @c data to @c m_buffer, plus a tab. + void escape_field_to_buffer(std::string_view data); + + /// Append string representation for @c f to @c m_buffer. + /** This is for the general case, where the field may contain a value. + * + * Also appends a tab. The tab is meant to be a separator, not a terminator, + * so if you write any fields at all, you'll end up with one tab too many + * at the end of the buffer. + */ + template + std::enable_if_t::always_null> + append_to_buffer(Field const &f) + { + // We append each field, terminated by a tab. That will leave us with + // one tab too many, assuming we write any fields at all; we remove that + // at the end. + if (is_null(f)) + { + // Easy. Append null and tab in one go. + m_buffer.append(null_field); + } + else + { + // Convert f into m_buffer. + + using traits = string_traits; + auto const budget{estimate_buffer(f)}; + auto const offset{std::size(m_buffer)}; + + if constexpr (std::is_arithmetic_v) + { + // Specially optimised for "safe" types, which never need any + // escaping. Convert straight into m_buffer. + + // The budget we get from size_buffer() includes room for the trailing + // zero, which we must remove. But we're also inserting tabs between + // fields, so we re-purpose the extra byte for that. + auto const total{offset + budget}; + m_buffer.resize(total); + auto const data{m_buffer.data()}; + char *const end{traits::into_buf(data + offset, data + total, f)}; + *(end - 1) = '\t'; + // Shrink to fit. Keep the tab though. + m_buffer.resize(static_cast(end - data)); + } + else if constexpr ( + std::is_same_v or + std::is_same_v or + std::is_same_v) + { + // This string may need escaping. + m_field_buf.resize(budget); + escape_field_to_buffer(f); + } + else + { + // This field needs to be converted to a string, and after that, + // escaped as well. + m_field_buf.resize(budget); + auto const data{m_field_buf.data()}; + escape_field_to_buffer( + traits::to_buf(data, data + std::size(m_field_buf), f)); + } + } + } + + /// Append string representation for a null field to @c m_buffer. + /** This special case is for types which are always null. + * + * Also appends a tab. The tab is meant to be a separator, not a terminator, + * so if you write any fields at all, you'll end up with one tab too many + * at the end of the buffer. + */ + template + std::enable_if_t::always_null> + append_to_buffer(Field const &) + { + m_buffer.append(null_field); + } + + /// Write raw COPY line into @c m_buffer, based on a container of fields. + template + std::enable_if_t> + fill_buffer(Container const &c) + { + // To avoid unnecessary allocations and deallocations, we run through c + // twice: once to determine how much buffer space we may need, and once to + // actually write it into the buffer. + std::size_t budget{0}; + for (auto const &f : c) budget += estimate_buffer(f); + m_buffer.reserve(budget); + for (auto const &f : c) append_to_buffer(f); + } + + /// Estimate how many buffer bytes we need to write tuple. + template + static std::size_t + budget_tuple(Tuple const &t, std::index_sequence) + { + return (estimate_buffer(std::get(t)) + ...); + } + + /// Write tuple of fields to @c m_buffer. + template + void append_tuple(Tuple const &t, std::index_sequence) + { + (append_to_buffer(std::get(t)), ...); + } + + /// Write raw COPY line into @c m_buffer, based on a tuple of fields. + template void fill_buffer(std::tuple const &t) + { + using indexes = std::make_index_sequence; + + m_buffer.reserve(budget_tuple(t, indexes{})); + append_tuple(t, indexes{}); + } + + /// Write raw COPY line into @c m_buffer, based on varargs fields. + template void fill_buffer(const Ts &...fields) + { + (..., append_to_buffer(fields)); + } + + constexpr static std::string_view s_classname{"stream_to"}; +}; + + +template +inline stream_to::stream_to( + transaction_base &tx, std::string_view table_name, Columns const &columns) : + stream_to{tx, table_name, std::begin(columns), std::end(columns)} +{} + + +template +inline stream_to::stream_to( + transaction_base &tx, std::string_view table_name, Iter columns_begin, + Iter columns_end) : + stream_to{ + tx, + tx.quote_name( + table_name, + separated_list(",", columns_begin, columns_end, [&tx](auto col) { + return tx.quote_name(*col); + }))} +{} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/subtransaction b/ext/libpqxx-7.7.3/include/pqxx/subtransaction new file mode 100644 index 000000000..e0d154903 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/subtransaction @@ -0,0 +1,8 @@ +/** pqxx::subtransaction class. + * + * pqxx::subtransaction is a nested transaction, i.e. one inside a transaction. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/subtransaction.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/subtransaction.hxx b/ext/libpqxx-7.7.3/include/pqxx/subtransaction.hxx new file mode 100644 index 000000000..e66b7a7a8 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/subtransaction.hxx @@ -0,0 +1,96 @@ +/* Definition of the pqxx::subtransaction class. + * + * pqxx::subtransaction is a nested transaction, i.e. one within a transaction. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/subtransaction instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_SUBTRANSACTION +#define PQXX_H_SUBTRANSACTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/dbtransaction.hxx" + +namespace pqxx +{ +/** + * @ingroup transactions + */ +/// "Transaction" nested within another transaction +/** A subtransaction can be executed inside a backend transaction, or inside + * another subtransaction. This can be useful when, for example, statements in + * a transaction may harmlessly fail and you don't want them to abort the + * entire transaction. Here's an example of how a temporary table may be + * dropped before re-creating it, without failing if the table did not exist: + * + * ```cxx + * void do_job(connection &C) + * { + * string const temptable = "fleetingtable"; + * + * work W(C, "do_job"); + * do_firstpart(W); + * + * // Attempt to delete our temporary table if it already existed. + * try + * { + * subtransaction S(W, "droptemp"); + * S.exec0("DROP TABLE " + temptable); + * S.commit(); + * } + * catch (undefined_table const &) + * { + * // Table did not exist. Which is what we were hoping to achieve anyway. + * // Carry on without regrets. + * } + * + * // S may have gone into a failed state and been destroyed, but the + * // upper-level transaction W is still fine. We can continue to use it. + * W.exec0("CREATE TEMP TABLE " + temptable + "(bar integer, splat + * varchar)"); + * + * do_lastpart(W); + * } + * ``` + * + * (This is just an example. If you really wanted to do drop a table without + * an error if it doesn't exist, you'd use DROP TABLE IF EXISTS.) + * + * There are no isolation levels inside a transaction. They are not needed + * because all actions within the same backend transaction are always performed + * sequentially anyway. + * + * @warning While the subtransaction is "live," you cannot execute queries or + * open streams etc. on its parent transaction. A transaction can have at most + * one object of a type derived from @ref pqxx::transaction_focus active on it + * at a time. + */ +class PQXX_LIBEXPORT subtransaction : public transaction_focus, + public dbtransaction +{ +public: + /// Nest a subtransaction nested in another transaction. + explicit subtransaction(dbtransaction &t, std::string_view tname = ""sv); + + /// Nest a subtransaction in another subtransaction. + explicit subtransaction(subtransaction &t, std::string_view name = ""sv); + + virtual ~subtransaction() noexcept override; + +private: + std::string quoted_name() const + { + return quote_name(transaction_focus::name()); + } + virtual void do_commit() override; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/time b/ext/libpqxx-7.7.3/include/pqxx/time new file mode 100644 index 000000000..85df05744 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/time @@ -0,0 +1,6 @@ +/** Date/time string conversions. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/time.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/time.hxx b/ext/libpqxx-7.7.3/include/pqxx/time.hxx new file mode 100644 index 000000000..effed05e0 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/time.hxx @@ -0,0 +1,88 @@ +/** Support for date/time values. + * + * At the moment this supports dates, but not times. + */ +#ifndef PQXX_H_TIME +#define PQXX_H_TIME + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + +#include "pqxx/internal/concat.hxx" +#include "pqxx/strconv.hxx" + + +#if defined(PQXX_HAVE_YEAR_MONTH_DAY) + +namespace pqxx +{ +using namespace std::literals; + +template<> +struct nullness + : no_null +{}; + + +/// String representation for a Gregorian date in ISO-8601 format. +/** @warning Experimental. There may still be design problems, particularly + * when it comes to BC years. + * + * PostgreSQL supports a choice of date formats, but libpqxx does not. The + * other formats in turn support a choice of "month before day" versus "day + * before month," meaning that it's not necessarily known which format a given + * date is supposed to be. So I repeat: ISO-8601-style format only! + * + * Invalid dates will not convert. This includes February 29 on non-leap + * years, which is why it matters that `year_month_day` represents a + * _Gregorian_ date. + * + * The range of years is limited. At the time of writing, PostgreSQL 14 + * supports years from 4713 BC to 294276 AD inclusive, and C++20 supports + * a range of 32767 BC to 32767 AD inclusive. So in practice, years must fall + * between 4713 BC and 32767 AD, inclusive. + * + * @warning Support for BC (or BCE) years is still experimental. I still need + * confirmation on this issue: it looks as if C++ years are astronomical years, + * which means they have a Year Zero. Regular BC/AD years do not have a year + * zero, so the year 1 AD follows directly after 1 BC. + * + * So, what to our calendars (and to PostgreSQL) is the year "0001 BC" seems to + * count as year "0" in a `std::chrono::year_month_day`. The year 0001 AD is + * still equal to 1 as you'd expect, and all AD years work normally, but all + * years before then are shifted by one. For instance, the year 543 BC would + * be -542 in C++. + */ +template<> struct PQXX_LIBEXPORT string_traits +{ + [[nodiscard]] static zview + to_buf(char *begin, char *end, std::chrono::year_month_day const &value) + { + return generic_to_buf(begin, end, value); + } + + static char * + into_buf(char *begin, char *end, std::chrono::year_month_day const &value); + + [[nodiscard]] static std::chrono::year_month_day + from_string(std::string_view text); + + [[nodiscard]] static std::size_t + size_buffer(std::chrono::year_month_day const &) noexcept + { + static_assert(int{(std::chrono::year::min)()} >= -99999); + static_assert(int{(std::chrono::year::max)()} <= 99999); + return 5 + 1 + 2 + 1 + 2 + std::size(s_bc) + 1; + } + +private: + /// The "BC" suffix for years before 1 AD. + static constexpr std::string_view s_bc{" BC"sv}; +}; +} // namespace pqxx +#endif // PQXX_HAVE_YEAR_MONTH_DAY +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/transaction b/ext/libpqxx-7.7.3/include/pqxx/transaction new file mode 100644 index 000000000..a7ae39d43 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/transaction @@ -0,0 +1,8 @@ +/** pqxx::transaction class. + * + * pqxx::transaction represents a standard database transaction. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/transaction.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/transaction.hxx b/ext/libpqxx-7.7.3/include/pqxx/transaction.hxx new file mode 100644 index 000000000..e90917e38 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/transaction.hxx @@ -0,0 +1,108 @@ +/* Definition of the pqxx::transaction class. + * pqxx::transaction represents a standard database transaction. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/transaction instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_TRANSACTION +#define PQXX_H_TRANSACTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/dbtransaction.hxx" + +namespace pqxx::internal +{ +/// Helper base class for the @ref transaction class template. +class PQXX_LIBEXPORT basic_transaction : public dbtransaction +{ +protected: + basic_transaction( + connection &c, zview begin_command, std::string_view tname); + basic_transaction(connection &c, zview begin_command, std::string &&tname); + basic_transaction(connection &c, zview begin_command); + + virtual ~basic_transaction() noexcept override = 0; + +private: + virtual void do_commit() override; +}; +} // namespace pqxx::internal + + +namespace pqxx +{ +/** + * @ingroup transactions + */ +//@{ + +/// Standard back-end transaction, templatised on isolation level. +/** This is the type you'll normally want to use to represent a transaction on + * the database. + * + * Usage example: double all wages. + * + * ```cxx + * extern connection C; + * work T(C); + * try + * { + * T.exec0("UPDATE employees SET wage=wage*2"); + * T.commit(); // NOTE: do this inside try block + * } + * catch (exception const &e) + * { + * cerr << e.what() << endl; + * T.abort(); // Usually not needed; same happens when T's life ends. + * } + * ``` + */ +template< + isolation_level ISOLATION = isolation_level::read_committed, + write_policy READWRITE = write_policy::read_write> +class transaction final : public internal::basic_transaction +{ +public: + /// Begin a transaction. + /** + * @param c Connection for this transaction to operate on. + * @param tname Optional name for transaction. Must begin with a letter and + * may contain letters and digits only. + */ + transaction(connection &c, std::string_view tname) : + internal::basic_transaction{ + c, internal::begin_cmd, tname} + {} + + /// Begin a transaction. + /** + * @param c Connection for this transaction to operate on. + * may contain letters and digits only. + */ + explicit transaction(connection &c) : + internal::basic_transaction{ + c, internal::begin_cmd} + {} + + virtual ~transaction() noexcept override { close(); } +}; + + +/// The default transaction type. +using work = transaction<>; + +/// Read-only transaction. +using read_transaction = + transaction; + +//@} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/transaction_base b/ext/libpqxx-7.7.3/include/pqxx/transaction_base new file mode 100644 index 000000000..c39219aac --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/transaction_base @@ -0,0 +1,9 @@ +/** Base for the transaction classes. + * + * pqxx::transaction_base defines the interface for any abstract class that + * represents a database transaction. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/transaction_base.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/transaction_base.hxx b/ext/libpqxx-7.7.3/include/pqxx/transaction_base.hxx new file mode 100644 index 000000000..4363cc56a --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/transaction_base.hxx @@ -0,0 +1,810 @@ +/* Common code and definitions for the transaction classes. + * + * pqxx::transaction_base defines the interface for any abstract class that + * represents a database transaction. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/transaction_base instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_TRANSACTION_BASE +#define PQXX_H_TRANSACTION_BASE + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + +/* End-user programs need not include this file, unless they define their own + * transaction classes. This is not something the typical program should want + * to do. + * + * However, reading this file is worthwhile because it defines the public + * interface for the available transaction classes such as transaction and + * nontransaction. + */ + +#include "pqxx/connection.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/encoding_group.hxx" +#include "pqxx/isolation.hxx" +#include "pqxx/result.hxx" +#include "pqxx/row.hxx" +#include "pqxx/stream_from.hxx" +#include "pqxx/util.hxx" + +namespace pqxx::internal::gate +{ +class transaction_subtransaction; +class transaction_sql_cursor; +class transaction_stream_to; +class transaction_transaction_focus; +} // namespace pqxx::internal::gate + + +namespace pqxx +{ +using namespace std::literals; + + +class transaction_focus; + + +/** + * @defgroup transactions Transaction classes + * + * All database access goes through instances of these classes. + * However, not all implementations of this interface need to provide full + * transactional integrity. + * + * Several implementations of this interface are shipped with libpqxx, + * including the plain transaction class, the entirely unprotected + * nontransaction, and the more cautious robusttransaction. + */ + +/// Interface definition (and common code) for "transaction" classes. +/** + * @ingroup transactions + * + * Abstract base class for all transaction types. + */ +class PQXX_LIBEXPORT PQXX_NOVTABLE transaction_base +{ +public: + transaction_base() = delete; + transaction_base(transaction_base const &) = delete; + transaction_base(transaction_base &&) = delete; + transaction_base &operator=(transaction_base const &) = delete; + transaction_base &operator=(transaction_base &&) = delete; + + virtual ~transaction_base() = 0; + + /// Commit the transaction. + /** Make the effects of this transaction definite. If you destroy a + * transaction without invoking its @ref commit() first, that will implicitly + * abort it. (For the @ref nontransaction class though, "commit" and "abort" + * really don't do anything, hence its name.) + * + * There is, however, a minute risk that you might lose your connection to + * the database at just the wrong moment here. In that case, libpqxx may be + * unable to determine whether the database was able to complete the + * transaction, or had to roll it back. In that scenario, @ref commit() will + * throw an in_doubt_error. There is a different transaction class called + * @ref robusttransaction which takes some special precautions to reduce this + * risk. + */ + void commit(); + + /// Abort the transaction. + /** No special effort is required to call this function; it will be called + * implicitly when the transaction is destructed. + */ + void abort(); + + /** + * @ingroup escaping-functions + * + * Use these when writing SQL queries that incorporate C++ values as SQL + * constants. + * + * The functions you see here are just convenience shortcuts to the same + * functions on the connection object. + */ + //@{ + /// Escape string for use as SQL string literal in this transaction. + template [[nodiscard]] auto esc(ARGS &&...args) const + { + return conn().esc(std::forward(args)...); + } + + /// Escape binary data for use as SQL string literal in this transaction. + /** Raw, binary data is treated differently from regular strings. Binary + * strings are never interpreted as text, so they may safely include byte + * values or byte sequences that don't happen to represent valid characters + * in the character encoding being used. + * + * The binary string does not stop at the first zero byte, as is the case + * with textual strings. Instead, it may contain zero bytes anywhere. If + * it happens to contain bytes that look like quote characters, or other + * things that can disrupt their use in SQL queries, they will be replaced + * with special escape sequences. + */ + template [[nodiscard]] auto esc_raw(ARGS &&...args) const + { + return conn().esc_raw(std::forward(args)...); + } + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string + unesc_raw(zview text) const + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return conn().unesc_raw(text); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard]] std::basic_string unesc_bin(zview text) + { + return conn().unesc_bin(text); + } + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string + unesc_raw(char const *text) const + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return conn().unesc_raw(text); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard]] std::basic_string unesc_bin(char const text[]) + { + return conn().unesc_bin(text); + } + + /// Represent object as SQL string, including quoting & escaping. + /** Nulls are recognized and represented as SQL nulls. */ + template [[nodiscard]] std::string quote(T const &t) const + { + return conn().quote(t); + } + + [[deprecated( + "Use std::basic_string instead of binarystring.")]] std::string + quote(binarystring const &t) const + { + return conn().quote(t.bytes_view()); + } + + /// Binary-escape and quote a binary string for use as an SQL constant. + [[deprecated("Use quote(std::basic_string_view).")]] std::string + quote_raw(unsigned char const bin[], std::size_t len) const + { + return quote(binary_cast(bin, len)); + } + + /// Binary-escape and quote a binary string for use as an SQL constant. + [[deprecated("Use quote(std::basic_string_view).")]] std::string + quote_raw(zview bin) const; + +#if defined(PQXX_HAVE_CONCEPTS) + /// Binary-escape and quote a binary string for use as an SQL constant. + /** For binary data you can also just use @ref quote(data). */ + template + [[nodiscard]] std::string quote_raw(DATA const &data) const + { + return conn().quote_raw(data); + } +#endif + + /// Escape an SQL identifier for use in a query. + [[nodiscard]] std::string quote_name(std::string_view identifier) const + { + return conn().quote_name(identifier); + } + + /// Escape string for literal LIKE match. + [[nodiscard]] std::string + esc_like(std::string_view bin, char escape_char = '\\') const + { + return conn().esc_like(bin, escape_char); + } + //@} + + /** + * @name Command execution + * + * There are many functions for executing (or "performing") a command (or + * "query"). This is the most fundamental thing you can do with the library, + * and you always do it from a transaction class. + * + * Command execution can throw many types of exception, including sql_error, + * broken_connection, and many sql_error subtypes such as + * feature_not_supported or insufficient_privilege. But any exception thrown + * by the C++ standard library may also occur here. All exceptions you will + * see libpqxx throw are derived from std::exception. + * + * One unusual feature in libpqxx is that you can give your query a name or + * description. This does not mean anything to the database, but sometimes + * it can help libpqxx produce more helpful error messages, making problems + * in your code easier to debug. + * + * Many of the execution functions used to accept a `desc` argument, a + * human-readable description of the statement for use in error messages. + * This could make failures easier to debug. Future versions will use + * C++20's `std::source_location` to identify the failing statement. + */ + //@{ + + /// Execute a command. + /** + * @param query Query or command to execute. + * @param desc Optional identifier for query, to help pinpoint SQL errors. + * @return A result set describing the query's or command's result. + */ + [[deprecated("The desc parameter is going away.")]] result + exec(std::string_view query, std::string_view desc); + + /// Execute a command. + /** + * @param query Query or command to execute. + * @return A result set describing the query's or command's result. + */ + result exec(std::string_view query) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return exec(query, std::string_view{}); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Execute a command. + /** + * @param query Query or command to execute. + * @param desc Optional identifier for query, to help pinpoint SQL errors. + * @return A result set describing the query's or command's result. + */ + [[deprecated( + "Pass your query as a std::string_view, not stringstream.")]] result + exec(std::stringstream const &query, std::string_view desc) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return exec(query.str(), desc); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Execute command, which should return zero rows of data. + /** Works like @ref exec, but fails if the result contains data. It still + * returns a result, however, which may contain useful metadata. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + [[deprecated("The desc parameter is going away.")]] result + exec0(zview query, std::string_view desc) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return exec_n(0, query, desc); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Execute command, which should return zero rows of data. + /** Works like @ref exec, but fails if the result contains data. It still + * returns a result, however, which may contain useful metadata. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + result exec0(zview query) { return exec_n(0, query); } + + /// Execute command returning a single row of data. + /** Works like @ref exec, but requires the result to contain exactly one row. + * The row can be addressed directly, without the need to find the first row + * in a result set. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + [[deprecated("The desc parameter is going away.")]] row + exec1(zview query, std::string_view desc) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return exec_n(1, query, desc).front(); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Execute command returning a single row of data. + /** Works like @ref exec, but requires the result to contain exactly one row. + * The row can be addressed directly, without the need to find the first row + * in a result set. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + row exec1(zview query) { return exec_n(1, query).front(); } + + /// Execute command, expect given number of rows. + /** Works like @ref exec, but checks that the result has exactly the expected + * number of rows. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + [[deprecated("The desc parameter is going away.")]] result + exec_n(result::size_type rows, zview query, std::string_view desc); + + /// Execute command, expect given number of rows. + /** Works like @ref exec, but checks that the result has exactly the expected + * number of rows. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + result exec_n(result::size_type rows, zview query) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return exec_n(rows, query, std::string_view{}); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Perform query, expecting exactly 1 row with 1 field, and convert it. + /** This is convenience shorthand for querying exactly one value from the + * database. It returns that value, converted to the type you specify. + */ + template + [[deprecated("The desc parameter is going away.")]] TYPE + query_value(zview query, std::string_view desc) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + row const r{exec1(query, desc)}; +#include "pqxx/internal/ignore-deprecated-post.hxx" + if (std::size(r) != 1) + throw usage_error{internal::concat( + "Queried single value from result with ", std::size(r), " columns.")}; + return r[0].as(); + } + + /// Perform query, expecting exactly 1 row with 1 field, and convert it. + /** This is convenience shorthand for querying exactly one value from the + * database. It returns that value, converted to the type you specify. + */ + template TYPE query_value(zview query) + { + row const r{exec1(query)}; + if (std::size(r) != 1) + throw usage_error{internal::concat( + "Queried single value from result with ", std::size(r), " columns.")}; + return r[0].as(); + } + + /// Execute a query, and loop over the results row by row. + /** Converts the rows to `std::tuple`, of the column types you specify. + * + * Use this with a range-based "for" loop. It executes the query, and + * directly maps the resulting rows onto a `std::tuple` of the types you + * specify. It starts before all the data from the server is in, so if your + * network connection to the server breaks while you're iterating, you'll get + * an exception partway through. + * + * The stream lives entirely within the lifetime of the transaction. Make + * sure you destroy the stream before you destroy the transaction. Either + * iterate the stream all the way to the end, or destroy first the stream + * and then the transaction without touching either in any other way. Until + * the stream has finished, the transaction is in a special state where it + * cannot execute queries. + * + * As a special case, tuple may contain `std::string_view` fields, but the + * strings to which they point will only remain valid until you extract the + * next row. After that, the memory holding the string may be overwritten or + * deallocated. + * + * If any of the columns can be null, and the C++ type to which it translates + * does not have a null value, wrap the type in `std::optional` (or if + * you prefer, `std::shared_ptr` or `std::unique_ptr)`. These templates + * do recognise null values, and libpqxx will know how to convert to them. + * + * The connection is in a special state until the iteration finishes. So if + * it does not finish due to a `break` or a `return` or an exception, then + * the entire connection becomes effectively unusable. + * + * Querying in this way is faster than the `exec()` methods for larger + * results (but slower for small ones). You can start processing rows before + * the full result is in. Also, `stream()` scales better in terms of memory + * usage. Where @ref exec() reads the entire result into memory at once, + * `stream()` will read and process one row at at a time. + * + * Your query executes as part of a COPY command, not as a stand-alone query, + * so there are limitations to what you can do in the query. It can be + * either a SELECT or VALUES query; or an INSERT, UPDATE, or DELETE with a + * RETURNING clause. See the documentation for PostgreSQL's COPY command for + * the details: + * + * https://www.postgresql.org/docs/current/sql-copy.html + * + * Iterating in this way does require each of the field types you pass to be + * default-constructible, copy-constructible, and assignable. These + * requirements may be loosened once libpqxx moves on to C++20. + */ + template + [[nodiscard]] auto stream(std::string_view query) & + { + // Tricky: std::make_unique() supports constructors but not RVO functions. + return pqxx::internal::owning_stream_input_iteration{ + std::unique_ptr{ + new stream_from{stream_from::query(*this, query)}}}; + } + + // C++20: Concept like std::invocable, but without specifying param types. + /// Perform a streaming query, and for each result row, call `func`. + /** Here, `func` can be a function, a `std::function`, a lambda, or an + * object that supports the function call operator. Of course `func` must + * have an unambiguous signature; it can't be overloaded or generic. + * + * The `for_each` function executes `query` in a stream using + * @ref pqxx::stream_from. Every time a row of data comes in from the + * server, it converts the row's fields to the types of `func`'s respective + * parameters, and calls `func` with those values. + * + * This will not work for all queries, but straightforward `SELECT` and + * `UPDATE ... RETURNING` queries should work. Consult the documentation for + * @ref pqxx::stream_from and PostgreSQL's underlying `COPY` command for the + * full details. + * + * Streaming a query like this is likely to be slower than the @ref exec() + * functions for small result sets, but faster for large result sets. So if + * performance matters, you'll want to use `for_each` if you query large + * amounts of data, but not if you do lots of queries with small outputs. + */ + template + inline auto for_each(std::string_view query, CALLABLE &&func) + { + using param_types = + pqxx::internal::strip_types_t>; + param_types const *const sample{nullptr}; + auto data_stream{stream_like(query, sample)}; + for (auto const &fields : data_stream) std::apply(func, fields); + } + + /** + * @name Parameterized statements + * + * You'll often need parameters in the queries you execute: "select the + * car with this licence plate." If the parameter is a string, you need to + * quote it and escape any special characters inside it, or it may become a + * target for an SQL injection attack. If it's an integer (for example), + * you need to convert it to a string, but in the database's format, without + * locale-specific niceties like "," separators between the thousands. + * + * Parameterised statements are an easier and safer way to do this. They're + * like prepared statements, but for a single use. You don't need to name + * them, and you don't need to prepare them first. + * + * Your query will include placeholders like `$1` and `$2` etc. in the places + * where you want the arguments to go. Then, you pass the argument values + * and the actual query is constructed for you. + * + * Pass the exact right number of parameters, and in the right order. The + * parameters in the query don't have to be neatly ordered from `$1` to + * `$2` to `$3` - but you must pass the argument for `$1` first, the one + * for `$2` second, etc. + * + * @warning Beware of "nul" bytes. Any string you pass as a parameter will + * end at the first char with value zero. If you pass a string that contains + * a zero byte, the last byte in the value will be the one just before the + * zero. + */ + //@{ + /// Execute an SQL statement with parameters. + template result exec_params(zview query, Args &&...args) + { + params pp(args...); + return internal_exec_params(query, pp.make_c_params()); + } + + // Execute parameterised statement, expect a single-row result. + /** @throw unexpected_rows if the result does not consist of exactly one row. + */ + template row exec_params1(zview query, Args &&...args) + { + return exec_params_n(1, query, std::forward(args)...).front(); + } + + // Execute parameterised statement, expect a result with zero rows. + /** @throw unexpected_rows if the result contains rows. + */ + template result exec_params0(zview query, Args &&...args) + { + return exec_params_n(0, query, std::forward(args)...); + } + + // Execute parameterised statement, expect exactly a given number of rows. + /** @throw unexpected_rows if the result contains the wrong number of rows. + */ + template + result exec_params_n(std::size_t rows, zview query, Args &&...args) + { + auto const r{exec_params(query, std::forward(args)...)}; + check_rowcount_params(rows, std::size(r)); + return r; + } + //@} + + /** + * @name Prepared statements + * + * These are very similar to parameterised statements. The difference is + * that you prepare them in advance, giving them identifying names. You can + * then call them by these names, passing in the argument values appropriate + * for that call. + * + * You prepare a statement on the connection, using + * @ref pqxx::connection::prepare(). But you then call the statement in a + * transaction, using the functions you see here. + * + * Never try to prepare, execute, or unprepare a prepared statement manually + * using direct SQL queries when you also use the libpqxx equivalents. For + * any given statement, either prepare, manage, and execute it through the + * dedicated libpqxx functions; or do it all directly in SQL. Don't mix the + * two, or the code may get confused. + * + * See \ref prepared for a full discussion. + * + * @warning Beware of "nul" bytes. Any string you pass as a parameter will + * end at the first char with value zero. If you pass a string that contains + * a zero byte, the last byte in the value will be the one just before the + * zero. If you need a zero byte, you're dealing with binary strings, not + * regular strings. Represent binary strings on the SQL side as `BYTEA` + * (or as large objects). On the C++ side, use types like + * `std::basic_string` or `std::basic_string_view` + * or (in C++20) `std::vector`. Also, consider large objects on + * the SQL side and @ref blob on the C++ side. + */ + //@{ + + /// Execute a prepared statement, with optional arguments. + template + result exec_prepared(zview statement, Args &&...args) + { + params pp(args...); + return internal_exec_prepared(statement, pp.make_c_params()); + } + + /// Execute a prepared statement, and expect a single-row result. + /** @throw pqxx::unexpected_rows if the result was not exactly 1 row. + */ + template + row exec_prepared1(zview statement, Args &&...args) + { + return exec_prepared_n(1, statement, std::forward(args)...).front(); + } + + /// Execute a prepared statement, and expect a result with zero rows. + /** @throw pqxx::unexpected_rows if the result contained rows. + */ + template + result exec_prepared0(zview statement, Args &&...args) + { + return exec_prepared_n(0, statement, std::forward(args)...); + } + + /// Execute a prepared statement, expect a result with given number of rows. + /** @throw pqxx::unexpected_rows if the result did not contain exactly the + * given number of rows. + */ + template + result + exec_prepared_n(result::size_type rows, zview statement, Args &&...args) + { + auto const r{exec_prepared(statement, std::forward(args)...)}; + check_rowcount_prepared(statement, rows, std::size(r)); + return r; + } + + //@} + + /** + * @name Error/warning output + */ + //@{ + /// Have connection process a warning message. + void process_notice(char const msg[]) const { m_conn.process_notice(msg); } + /// Have connection process a warning message. + void process_notice(zview msg) const { m_conn.process_notice(msg); } + //@} + + /// The connection in which this transaction lives. + [[nodiscard]] constexpr connection &conn() const noexcept { return m_conn; } + + /// Set session variable using SQL "SET" command. + /** @deprecated To set a transaction-local variable, execute an SQL `SET` + * command. To set a session variable, use the connection's + * @ref set_session_var function. + * + * @warning When setting a string value, you must make sure that the string + * is "safe." If you call @ref quote() on the string, it will return a + * safely escaped and quoted version for use as an SQL literal. + * + * @warning This function executes SQL. Do not try to set or get variables + * while a pipeline or table stream is active. + * + * @param var The variable to set. + * @param value The new value to store in the variable. This can be any SQL + * expression. + */ + [[deprecated( + "Set transaction-local variables using SQL SET statements.")]] void + set_variable(std::string_view var, std::string_view value); + + /// Read session variable using SQL "SHOW" command. + /** @warning This executes SQL. Do not try to set or get variables while a + * pipeline or table stream is active. + */ + [[deprecated("Read variables using SQL SHOW statements.")]] std::string + get_variable(std::string_view); + + // C++20: constexpr. + /// Transaction name, if you passed one to the constructor; or empty string. + [[nodiscard]] std::string_view name() const &noexcept { return m_name; } + +protected: + /// Create a transaction (to be called by implementation classes only). + /** The name, if nonempty, must begin with a letter and may contain letters + * and digits only. + */ + transaction_base( + connection &c, std::string_view tname, + std::shared_ptr rollback_cmd) : + m_conn{c}, m_name{tname}, m_rollback_cmd{rollback_cmd} + {} + + /// Create a transaction (to be called by implementation classes only). + /** Its rollback command will be "ROLLBACK". + * + * The name, if nonempty, must begin with a letter and may contain letters + * and digits only. + */ + transaction_base(connection &c, std::string_view tname); + + /// Create a transaction (to be called by implementation classes only). + explicit transaction_base(connection &c); + + /// Register this transaction with the connection. + void register_transaction(); + + /// End transaction. To be called by implementing class' destructor. + void close() noexcept; + + /// To be implemented by derived implementation class: commit transaction. + virtual void do_commit() = 0; + + /// Transaction type-specific way of aborting a transaction. + /** @warning This will become "final", since this function can be called + * from the implementing class destructor. + */ + virtual void do_abort(); + + /// Set the rollback command. + void set_rollback_cmd(std::shared_ptr cmd) + { + m_rollback_cmd = cmd; + } + + /// Execute query on connection directly. + result direct_exec(std::string_view, std::string_view desc = ""sv); + result + direct_exec(std::shared_ptr, std::string_view desc = ""sv); + +private: + enum class status + { + active, + aborted, + committed, + in_doubt + }; + + PQXX_PRIVATE void check_pending_error(); + + result + internal_exec_prepared(zview statement, internal::c_params const &args); + + result internal_exec_params(zview query, internal::c_params const &args); + + /// Throw unexpected_rows if prepared statement returned wrong no. of rows. + void check_rowcount_prepared( + zview statement, result::size_type expected_rows, + result::size_type actual_rows); + + /// Throw unexpected_rows if wrong row count from parameterised statement. + void + check_rowcount_params(std::size_t expected_rows, std::size_t actual_rows); + + /// Describe this transaction to humans, e.g. "transaction 'foo'". + [[nodiscard]] std::string description() const; + + friend class pqxx::internal::gate::transaction_transaction_focus; + PQXX_PRIVATE void register_focus(transaction_focus *); + PQXX_PRIVATE void unregister_focus(transaction_focus *) noexcept; + PQXX_PRIVATE void register_pending_error(zview) noexcept; + PQXX_PRIVATE void register_pending_error(std::string &&) noexcept; + + /// Like @ref stream(), but takes a tuple rather than a parameter pack. + template + auto stream_like(std::string_view query, std::tuple const *) + { + return stream(query); + } + + connection &m_conn; + + /// Current "focus": a pipeline, a nested transaction, a stream... + /** This pointer is used for only one purpose: sanity checks against mistakes + * such as opening one while another is still active. + */ + transaction_focus const *m_focus = nullptr; + + status m_status = status::active; + bool m_registered = false; + std::string m_name; + std::string m_pending_error; + + /// SQL command for aborting this type of transaction. + std::shared_ptr m_rollback_cmd; + + static constexpr std::string_view s_type_name{"transaction"sv}; +}; + + +// C++20: Can borrowed_range help? +/// Forbidden specialisation: underlying buffer immediately goes out of scope. +template<> +std::string_view transaction_base::query_value( + zview query, std::string_view desc) = delete; +/// Forbidden specialisation: underlying buffer immediately goes out of scope. +template<> +zview transaction_base::query_value( + zview query, std::string_view desc) = delete; + +} // namespace pqxx + + +namespace pqxx::internal +{ +/// The SQL command for starting a given type of transaction. +template +extern const zview begin_cmd; + +// These are not static members, so "constexpr" does not imply "inline". +template<> +inline constexpr zview begin_cmd{ + "BEGIN"_zv}; +template<> +inline constexpr zview begin_cmd{ + "BEGIN READ ONLY"_zv}; +template<> +inline constexpr zview begin_cmd{ + "BEGIN ISOLATION LEVEL REPEATABLE READ"_zv}; +template<> +inline constexpr zview begin_cmd{ + "BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY"_zv}; +template<> +inline constexpr zview begin_cmd{ + "BEGIN ISOLATION LEVEL SERIALIZABLE"_zv}; +template<> +inline constexpr zview begin_cmd{ + "BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY"_zv}; +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/transaction_focus b/ext/libpqxx-7.7.3/include/pqxx/transaction_focus new file mode 100644 index 000000000..fe78a9bcc --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/transaction_focus @@ -0,0 +1,7 @@ +/** + * Transaction focus: types which monopolise a transaction's attention. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/types.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/transaction_focus.hxx b/ext/libpqxx-7.7.3/include/pqxx/transaction_focus.hxx new file mode 100644 index 000000000..0707e3cc4 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/transaction_focus.hxx @@ -0,0 +1,89 @@ +/** Transaction focus: types which monopolise a transaction's attention. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_TRANSACTION_FOCUS +#define PQXX_H_TRANSACTION_FOCUS + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/util.hxx" + +namespace pqxx +{ +/// Base class for things that monopolise a transaction's attention. +/** You probably won't need to use this class. But it can be useful to _know_ + * that a given libpqxx class is derived from it. + * + * Pipelines, SQL statements, and data streams are examples of classes derived + * from `transaction_focus`. For any given transaction, only one object of + * such a class can be active at any given time. + */ +class PQXX_LIBEXPORT transaction_focus +{ +public: + transaction_focus( + transaction_base &t, std::string_view cname, std::string_view oname) : + m_trans{t}, m_classname{cname}, m_name{oname} + {} + + transaction_focus( + transaction_base &t, std::string_view cname, std::string &&oname) : + m_trans{t}, m_classname{cname}, m_name{std::move(oname)} + {} + + transaction_focus(transaction_base &t, std::string_view cname) : + m_trans{t}, m_classname{cname} + {} + + transaction_focus() = delete; + transaction_focus(transaction_focus const &) = delete; + transaction_focus &operator=(transaction_focus const &) = delete; + + /// Class name, for human consumption. + [[nodiscard]] constexpr std::string_view classname() const noexcept + { + return m_classname; + } + + /// Name for this object, if the caller passed one; empty string otherwise. + [[nodiscard]] std::string_view name() const &noexcept { return m_name; } + + [[nodiscard]] std::string description() const + { + return pqxx::internal::describe_object(m_classname, m_name); + } + + /// Can't move a transaction_focus. + /** Moving the transaction_focus would break the transaction's reference back + * to the object. + */ + transaction_focus(transaction_focus &&) = delete; + + /// Can't move a transaction_focus. + /** Moving the transaction_focus would break the transaction's reference back + * to the object. + */ + transaction_focus &operator=(transaction_focus &&) = delete; + +protected: + void register_me(); + void unregister_me() noexcept; + void reg_pending_error(std::string const &) noexcept; + bool registered() const noexcept { return m_registered; } + + transaction_base &m_trans; + +private: + bool m_registered = false; + std::string_view m_classname; + std::string m_name; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/transactor b/ext/libpqxx-7.7.3/include/pqxx/transactor new file mode 100644 index 000000000..29d1b9640 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/transactor @@ -0,0 +1,8 @@ +/** pqxx::transactor class. + * + * pqxx::transactor is a framework-style wrapper for safe transactions. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/transactor.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/transactor.hxx b/ext/libpqxx-7.7.3/include/pqxx/transactor.hxx new file mode 100644 index 000000000..eefd04ba1 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/transactor.hxx @@ -0,0 +1,147 @@ +/* Transactor framework, a wrapper for safely retryable transactions. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/transactor instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_TRANSACTOR +#define PQXX_H_TRANSACTOR + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + +#include "pqxx/connection.hxx" +#include "pqxx/transaction.hxx" + +namespace pqxx +{ +/** + * @defgroup transactor Transactor framework + * + * Sometimes a transaction can fail for completely transient reasons, such as a + * conflict with another transaction in SERIALIZABLE isolation. The right way + * to handle those failures is often just to re-run the transaction from + * scratch. + * + * For example, your REST API might be handling each HTTP request in its own + * database transaction, and if this kind of transient failure happens, you + * simply want to "replay" the whole request, in a fresh transaction. + * + * You won't necessarily want to execute the exact same SQL commands with the + * exact same data. Some of your SQL statements may depend on state that can + * vary between retries. Data in the database may already have changed, for + * instance. So instead of dumbly replaying the SQL, you re-run the same + * application code that produced those SQL commands, from the start. + * + * The transactor framework makes it a little easier for you to do this safely, + * and avoid typical pitfalls. You encapsulate the work that you want to do + * into a callable that you pass to the @ref perform function. + * + * Here's how it works. You write your transaction code as a lambda or + * function, which creates its own transaction object, does its work, and + * commits at the end. You pass that callback to @ref pqxx::perform, which + * runs it for you. + * + * If there's a failure inside your callback, there will be an exception. Your + * transaction object goes out of scope and gets destroyed, so that it aborts + * implicitly. Seeing this, @ref perform tries running your callback again. It + * stops doing that when the callback succeeds, or when it has failed too many + * times, or when there's an error that leaves the database in an unknown + * state, such as a lost connection just while we're waiting for the database + * to confirm a commit. It all depends on the type of exception. + * + * The callback takes no arguments. If you're using lambdas, the easy way to + * pass arguments is for the lambda to "capture" them from your variables. Or, + * if you're using functions, you may want to use `std::bind`. + * + * Once your callback succeeds, it can return a result, and @ref perform will + * return that result back to you. + */ +//@{ + +/// Simple way to execute a transaction with automatic retry. +/** + * Executes your transaction code as a callback. Repeats it until it completes + * normally, or it throws an error other than the few libpqxx-generated + * exceptions that the framework understands, or after a given number of failed + * attempts, or if the transaction ends in an "in-doubt" state. + * + * (An in-doubt state is one where libpqxx cannot determine whether the server + * finally committed a transaction or not. This can happen if the network + * connection to the server is lost just while we're waiting for its reply to + * a "commit" statement. The server may have completed the commit, or not, but + * it can't tell you because there's no longer a connection. + * + * Using this still takes a bit of care. If your callback makes use of data + * from the database, you'll probably have to query that data within your + * callback. If the attempt to perform your callback fails, and the framework + * tries again, you'll be in a new transaction and the data in the database may + * have changed under your feet. + * + * Also be careful about changing variables or data structures from within + * your callback. The run may still fail, and perhaps get run again. The + * ideal way to do it (in most cases) is to return your result from your + * callback, and change your program's data state only after @ref perform + * completes successfully. + * + * @param callback Transaction code that can be called with no arguments. + * @param attempts Maximum number of times to attempt performing callback. + * Must be greater than zero. + * @return Whatever your callback returns. + */ +template +inline auto perform(TRANSACTION_CALLBACK &&callback, int attempts = 3) + -> std::invoke_result_t +{ + if (attempts <= 0) + throw std::invalid_argument{ + "Zero or negative number of attempts passed to pqxx::perform()."}; + + for (; attempts > 0; --attempts) + { + try + { + return std::invoke(callback); + } + catch (in_doubt_error const &) + { + // Not sure whether transaction went through or not. The last thing in + // the world that we should do now is try again! + throw; + } + catch (statement_completion_unknown const &) + { + // Not sure whether our last statement succeeded. Don't risk running it + // again. + throw; + } + catch (broken_connection const &) + { + // Connection failed. May be worth retrying, if the transactor opens its + // own connection. + if (attempts <= 1) + throw; + continue; + } + catch (transaction_rollback const &) + { + // Some error that may well be transient, such as serialization failure + // or deadlock. Worth retrying. + if (attempts <= 1) + throw; + continue; + } + } + throw pqxx::internal_error{"No outcome reached on perform()."}; +} +} // namespace pqxx +//@} +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/types b/ext/libpqxx-7.7.3/include/pqxx/types new file mode 100644 index 000000000..23a5caae1 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/types @@ -0,0 +1,7 @@ +/** + * Basic typedefs and forward declarations. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/types.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/types.hxx b/ext/libpqxx-7.7.3/include/pqxx/types.hxx new file mode 100644 index 000000000..f95b598f8 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/types.hxx @@ -0,0 +1,173 @@ +/* Basic type aliases and forward declarations. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_TYPES +#define PQXX_H_TYPES + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include + +#if defined(PQXX_HAVE_CONCEPTS) && __has_include() +# include +#endif + + +namespace pqxx +{ +/// Number of rows in a result set. +using result_size_type = int; + +/// Difference between result sizes. +using result_difference_type = int; + +/// Number of fields in a row of database data. +using row_size_type = int; + +/// Difference between row sizes. +using row_difference_type = int; + +/// Number of bytes in a field of database data. +using field_size_type = std::size_t; + +/// Number of bytes in a large object. +using large_object_size_type = int64_t; + + +// Forward declarations, to help break compilation dependencies. +// These won't necessarily include all classes in libpqxx. +class binarystring; +class connection; +class const_result_iterator; +class const_reverse_result_iterator; +class const_reverse_row_iterator; +class const_row_iterator; +class dbtransaction; +class field; +class largeobjectaccess; +class notification_receiver; +struct range_error; +class result; +class row; +class stream_from; +class transaction_base; + +/// Marker for @ref stream_from constructors: "stream from table." +/** @deprecated Use @ref stream_from::table() instead. + */ +struct from_table_t +{}; + +/// Marker for @ref stream_from constructors: "stream from query." +/** @deprecated Use @ref stream_from::query() instead. + */ +struct from_query_t +{}; + + +/// Format code: is data text or binary? +/** Binary-compatible with libpq's format codes. + */ +enum class format : int +{ + text = 0, + binary = 1, +}; + + +/// Remove any constness, volatile, and reference-ness from a type. +/** @deprecated In C++20 we'll replace this with std::remove_cvref. + */ +template +using strip_t = std::remove_cv_t>; + + +#if defined(PQXX_HAVE_CONCEPTS) +/// The type of a container's elements. +/** At the time of writing there's a similar thing in `std::experimental`, + * which we may or may not end up using for this. + */ +template +using value_type = strip_t()))>; +#else // PQXX_HAVE_CONCEPTS +/// The type of a container's elements. +/** At the time of writing there's a similar thing in `std::experimental`, + * which we may or may not end up using for this. + */ +template +using value_type = strip_t()))>; +#endif // PQXX_HAVE_CONCEPTS + + +#if defined(PQXX_HAVE_CONCEPTS) +/// Concept: Any type that we can read as a string of `char`. +template +concept char_string = std::ranges::contiguous_range and + std::same_as < strip_t>, +char > ; + +/// Concept: Anything we can iterate to get things we can read as strings. +template +concept char_strings = + std::ranges::range and char_string>>; + +/// Concept: Anything we might want to treat as binary data. +template +concept potential_binary = std::ranges::contiguous_range and + (sizeof(value_type) == 1); +#endif // PQXX_HAVE_CONCEPTS + + +// C++20: Retire these compatibility definitions. +#if defined(PQXX_HAVE_CONCEPTS) + +/// Template argument type for a range. +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_RANGE_ARG std::ranges::range + +/// Template argument type for @ref char_string. +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_CHAR_STRING_ARG pqxx::char_string + +/// Template argument type for @ref char_strings +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_CHAR_STRINGS_ARG pqxx::char_strings + +#else // PQXX_HAVE_CONCEPTS + +/// Template argument type for a range. +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_RANGE_ARG typename + +/// Template argument type for @ref char_string. +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_CHAR_STRING_ARG typename + +/// Template argument type for @ref char_strings +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_CHAR_STRINGS_ARG typename + +#endif // PQXX_HAVE_CONCEPTS +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/util b/ext/libpqxx-7.7.3/include/pqxx/util new file mode 100644 index 000000000..6d85ab611 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/util @@ -0,0 +1,6 @@ +/** Various utility definitions for libpqxx. + */ +// Actual definitions in .hxx file so editors and such recognize file type +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/util.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/util.hxx b/ext/libpqxx-7.7.3/include/pqxx/util.hxx new file mode 100644 index 000000000..4aa5ecf57 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/util.hxx @@ -0,0 +1,521 @@ +/* Various utility definitions for libpqxx. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/util instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_UTIL +#define PQXX_H_UTIL + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __has_include() +# include +#endif + +#include "pqxx/except.hxx" +#include "pqxx/internal/encodings.hxx" +#include "pqxx/types.hxx" +#include "pqxx/version.hxx" + + +/// The home of all libpqxx classes, functions, templates, etc. +namespace pqxx +{} + +#include + + +/// Internal items for libpqxx' own use. Do not use these yourself. +namespace pqxx::internal +{ + +// C++20: Retire wrapper. +/// Same as `std::cmp_less`, or a workaround where that's not available. +template +inline constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept +{ +#if defined(PQXX_HAVE_CMP) + return std::cmp_less(lhs, rhs); +#else + // We need a variable just because lgtm.com gives off a false positive + // warning when we compare the values directly. It considers that a + // "self-comparison." + constexpr bool left_signed{std::is_signed_v}; + if constexpr (left_signed == std::is_signed_v) + return lhs < rhs; + else if constexpr (std::is_signed_v) + return (lhs <= 0) ? true : (std::make_unsigned_t(lhs) < rhs); + else + return (rhs <= 0) ? false : (lhs < std::make_unsigned_t(rhs)); +#endif +} + + +// C++20: Retire wrapper. +/// C++20 std::cmp_greater, or workaround if not available. +template +inline constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept +{ +#if defined(PQXX_HAVE_CMP) + return std::cmp_greater(lhs, rhs); +#else + return cmp_less(rhs, lhs); +#endif +} + + +// C++20: Retire wrapper. +/// C++20 std::cmp_less_equal, or workaround if not available. +template +inline constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept +{ +#if defined(PQXX_HAVE_CMP) + return std::cmp_less_equal(lhs, rhs); +#else + return not cmp_less(rhs, lhs); +#endif +} + + +// C++20: Retire wrapper. +/// C++20 std::cmp_greater_equal, or workaround if not available. +template +inline constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept +{ +#if defined(PQXX_HAVE_CMP) + return std::cmp_greater_equal(lhs, rhs); +#else + return not cmp_less(lhs, rhs); +#endif +} + + +/// Efficiently concatenate two strings. +/** This is a special case of concatenate(), needed because dependency + * management does not let us use that function here. + */ +[[nodiscard]] inline std::string cat2(std::string_view x, std::string_view y) +{ + std::string buf; + auto const xs{std::size(x)}, ys{std::size(y)}; + buf.resize(xs + ys); + x.copy(std::data(buf), xs); + y.copy(std::data(buf) + xs, ys); + return buf; +} +} // namespace pqxx::internal + + +namespace pqxx +{ +using namespace std::literals; + +/// Suppress compiler warning about an unused item. +template inline constexpr void ignore_unused(T &&...) noexcept +{} + + +/// Cast a numeric value to another type, or throw if it underflows/overflows. +/** Both types must be arithmetic types, and they must either be both integral + * or both floating-point types. + */ +template +inline TO check_cast(FROM value, std::string_view description) +{ + static_assert(std::is_arithmetic_v); + static_assert(std::is_arithmetic_v); + static_assert(std::is_integral_v == std::is_integral_v); + + // The rest of this code won't quite work for bool, but bool is trivially + // convertible to other arithmetic types as far as I can see. + if constexpr (std::is_same_v) + return static_cast(value); + + // Depending on our "if constexpr" conditions, this parameter may not be + // needed. Some compilers will warn. + ignore_unused(description); + + using from_limits = std::numeric_limits; + using to_limits = std::numeric_limits; + if constexpr (std::is_signed_v) + { + if constexpr (std::is_signed_v) + { + if (value < to_limits::lowest()) + throw range_error{internal::cat2("Cast underflow: "sv, description)}; + } + else + { + // FROM is signed, but TO is not. Treat this as a special case, because + // there may not be a good broader type in which the compiler can even + // perform our check. + if (value < 0) + throw range_error{internal::cat2( + "Casting negative value to unsigned type: "sv, description)}; + } + } + else + { + // No need to check: the value is unsigned so can't fall below the range + // of the TO type. + } + + if constexpr (std::is_integral_v) + { + using unsigned_from = std::make_unsigned_t; + using unsigned_to = std::make_unsigned_t; + constexpr auto from_max{static_cast((from_limits::max)())}; + constexpr auto to_max{static_cast((to_limits::max)())}; + if constexpr (from_max > to_max) + { + if (internal::cmp_greater(value, to_max)) + throw range_error{internal::cat2("Cast overflow: "sv, description)}; + } + } + else if constexpr ((from_limits::max)() > (to_limits::max)()) + { + if (value > (to_limits::max)()) + throw range_error{internal::cat2("Cast overflow: ", description)}; + } + + return static_cast(value); +} + + +/** Check library version at link time. + * + * Ensures a failure when linking an application against a radically + * different libpqxx version than the one against which it was compiled. + * + * Sometimes application builds fail in unclear ways because they compile + * using headers from libpqxx version X, but then link against libpqxx + * binary version Y. A typical scenario would be one where you're building + * against a libpqxx which you have built yourself, but a different version + * is installed on the system. + * + * The check_library_version template is declared for any library version, + * but only actually defined for the version of the libpqxx binary against + * which the code is linked. + * + * If the library binary is a different version than the one declared in + * these headers, then this call will fail to link: there will be no + * definition for the function with these exact template parameter values. + * There will be a definition, but the version in the parameter values will + * be different. + */ +inline PQXX_PRIVATE void check_version() noexcept +{ + // There is no particular reason to do this here in @ref connection, except + // to ensure that every meaningful libpqxx client will execute it. The call + // must be in the execution path somewhere or the compiler won't try to link + // it. We can't use it to initialise a global or class-static variable, + // because a smart compiler might resolve it at compile time. + // + // On the other hand, we don't want to make a useless function call too + // often for performance reasons. A local static variable is initialised + // only on the definition's first execution. Compilers will be well + // optimised for this behaviour, so there's a minimal one-time cost. + static auto const version_ok{internal::PQXX_VERSION_CHECK()}; + ignore_unused(version_ok); +} + + +/// Descriptor of library's thread-safety model. +/** This describes what the library knows about various risks to thread-safety. + */ +struct PQXX_LIBEXPORT thread_safety_model +{ + /// Is the underlying libpq build thread-safe? + bool safe_libpq = false; + + /// Is Kerberos thread-safe? + /** @warning Is currently always `false`. + * + * If your application uses Kerberos, all accesses to libpqxx or Kerberos + * must be serialized. Confine their use to a single thread, or protect it + * with a global lock. + */ + bool safe_kerberos = false; + + /// A human-readable description of any thread-safety issues. + std::string description; +}; + + +/// Describe thread safety available in this build. +[[nodiscard]] PQXX_LIBEXPORT thread_safety_model describe_thread_safety(); + + +#if defined(PQXX_HAVE_CONCEPTS) +# define PQXX_POTENTIAL_BINARY_ARG pqxx::potential_binary +#else +# define PQXX_POTENTIAL_BINARY_ARG typename +#endif + + +/// Cast binary data to a type that libpqxx will recognise as binary. +/** There are many different formats for storing binary data in memory. You + * may have yours as a `std::string`, or a `std::vector`, or one of + * many other types. + * + * But for libpqxx to recognise your data as binary, it needs to be a + * `std::basic_string`, or a `std::basic_string_view`; + * or in C++20 or better, any contiguous block of `std::byte`. + * + * Use `binary_cast` as a convenience helper to cast your data as a + * `std::basic_string_view`. + * + * @warning There are two things you should be aware of! First, the data must + * be contiguous in memory. In C++20 the compiler will enforce this, but in + * C++17 it's your own problem. Second, you must keep the object where you + * store the actual data alive for as long as you might use this function's + * return value. + */ +template +std::basic_string_view binary_cast(TYPE const &data) +{ + static_assert(sizeof(value_type) == 1); + return { + reinterpret_cast( + const_cast const *>( + std::data(data))), + std::size(data)}; +} + + +#if defined(PQXX_HAVE_CONCEPTS) +template +concept char_sized = (sizeof(CHAR) == 1); +# define PQXX_CHAR_SIZED_ARG char_sized +#else +# define PQXX_CHAR_SIZED_ARG typename +#endif + +/// Construct a type that libpqxx will recognise as binary. +/** Takes a data pointer and a size, without being too strict about their + * types, and constructs a `std::basic_string_view` pointing to + * the same data. + * + * This makes it a little easier to turn binary data, in whatever form you + * happen to have it, into binary data as libpqxx understands it. + */ +template +std::basic_string_view binary_cast(CHAR const *data, SIZE size) +{ + static_assert(sizeof(CHAR) == 1); + return { + reinterpret_cast(data), + check_cast(size, "binary data size")}; +} + + +/// The "null" oid. +constexpr oid oid_none{0}; +} // namespace pqxx + + +/// Private namespace for libpqxx's internal use; do not access. +/** This namespace hides definitions internal to libpqxx. These are not + * supposed to be used by client programs, and they may change at any time + * without notice. + * + * Conversely, if you find something in this namespace tremendously useful, by + * all means do lodge a request for its publication. + * + * @warning Here be dragons! + */ +namespace pqxx::internal +{ +using namespace std::literals; + + +/// A safer and more generic replacement for `std::isdigit`. +/** Turns out `std::isdigit` isn't as easy to use as it sounds. It takes an + * `int`, but requires it to be nonnegative. Which means it's an outright + * liability on systems where `char` is signed. + */ +template inline constexpr bool is_digit(CHAR c) noexcept +{ + return (c >= '0') and (c <= '9'); +} + + +/// Describe an object for humans, based on class name and optional name. +/** Interprets an empty name as "no name given." + */ +[[nodiscard]] std::string +describe_object(std::string_view class_name, std::string_view name); + + +/// Check validity of registering a new "guest" in a "host." +/** The host might be e.g. a connection, and the guest a transaction. The + * host can only have one guest at a time, so it is an error to register a new + * guest while the host already has a guest. + * + * If the new registration is an error, this function throws a descriptive + * exception. + * + * Pass the old guest (if any) and the new guest (if any), for both, a type + * name (at least if the guest is not null), and optionally an object name + * (but which may be omitted if the caller did not assign one). + */ +void check_unique_register( + void const *old_guest, std::string_view old_class, std::string_view old_name, + void const *new_guest, std::string_view new_class, + std::string_view new_name); + + +/// Like @ref check_unique_register, but for un-registering a guest. +/** Pass the guest which was registered, as well as the guest which is being + * unregistered, so that the function can check that they are the same one. + */ +void check_unique_unregister( + void const *old_guest, std::string_view old_class, std::string_view old_name, + void const *new_guest, std::string_view new_class, + std::string_view new_name); + + +/// Compute buffer size needed to escape binary data for use as a BYTEA. +/** This uses the hex-escaping format. The return value includes room for the + * "\x" prefix. + */ +inline constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept +{ + return 2 + (2 * binary_bytes) + 1; +} + + +/// Compute binary size from the size of its escaped version. +/** Do not include a terminating zero in `escaped_bytes`. + */ +inline constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept +{ + return (escaped_bytes - 2) / 2; +} + + +// TODO: Use actual binary type for "data". +/// Hex-escape binary data into a buffer. +/** The buffer must be able to accommodate + * `size_esc_bin(std::size(binary_data))` bytes, and the function will write + * exactly that number of bytes into the buffer. This includes a trailing + * zero. + */ +void PQXX_LIBEXPORT +esc_bin(std::basic_string_view binary_data, char buffer[]) noexcept; + + +/// Hex-escape binary data into a std::string. +std::string PQXX_LIBEXPORT +esc_bin(std::basic_string_view binary_data); + + +/// Reconstitute binary data from its escaped version. +void PQXX_LIBEXPORT +unesc_bin(std::string_view escaped_data, std::byte buffer[]); + + +/// Reconstitute binary data from its escaped version. +std::basic_string + PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data); + + +/// Transitional: std::ssize(), or custom implementation if not available. +template auto ssize(T const &c) +{ +#if defined(__cpp_lib_ssize) && __cplusplus >= __cpp_lib_ssize + return std::ssize(c); +#else + using signed_t = std::make_signed_t; + return static_cast(std::size(c)); +#endif // __cpp_lib_ssize +} + + +/// Helper for determining a function's parameter types. +/** This function has no definition. It's not meant to be actually called. + * It's just there for pattern-matching in the compiler, so we can use its + * hypothetical return value. + */ +template +std::tuple args_f(RETURN (&func)(ARGS...)); + + +/// Helper for determining a `std::function`'s parameter types. +/** This function has no definition. It's not meant to be actually called. + * It's just there for pattern-matching in the compiler, so we can use its + * hypothetical return value. + */ +template +std::tuple args_f(std::function const &); + + +/// Helper for determining a member function's parameter types. +/** This function has no definition. It's not meant to be actually called. + * It's just there for pattern-matching in the compiler, so we can use its + * hypothetical return value. + */ +template +std::tuple member_args_f(RETURN (CLASS::*)(ARGS...)); + + +/// Helper for determining a const member function's parameter types. +/** This function has no definition. It's not meant to be actually called. + * It's just there for pattern-matching in the compiler, so we can use its + * hypothetical return value. + */ +template +std::tuple member_args_f(RETURN (CLASS::*)(ARGS...) const); + + +/// Helper for determining a callable type's parameter types. +/** This specialisation should work for lambdas. + * + * This function has no definition. It's not meant to be actually called. + * It's just there for pattern-matching in the compiler, so we can use its + * hypothetical return value. + */ +template +auto args_f(CALLABLE const &f) + -> decltype(member_args_f(&CALLABLE::operator())); + + +/// A callable's parameter types, as a tuple. +template +using args_t = decltype(args_f(std::declval())); + + +/// Helper: Apply `strip_t` to each of a tuple type's component types. +/** This function has no definition. It is not meant to be called, only to be + * used to deduce the right types. + */ +template +std::tuple...> strip_types(std::tuple const &); + + +/// Take a tuple type and apply @ref strip_t to its component types. +template +using strip_types_t = decltype(strip_types(std::declval())); +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/version b/ext/libpqxx-7.7.3/include/pqxx/version new file mode 100644 index 000000000..8dd5e48d4 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/version @@ -0,0 +1,7 @@ +/** libpqxx version info. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/version.hxx" +#include "pqxx/internal/header-post.hxx" + diff --git a/ext/libpqxx-7.7.3/include/pqxx/version.hxx b/ext/libpqxx-7.7.3/include/pqxx/version.hxx new file mode 100644 index 000000000..a159f1bed --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/version.hxx @@ -0,0 +1,55 @@ +/* Version info for libpqxx. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/version instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_VERSION + +# if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +# endif + +/// Full libpqxx version string. +# define PQXX_VERSION "7.7.3" +/// Library ABI version. +# define PQXX_ABI "7.7" + +/// Major version number. +# define PQXX_VERSION_MAJOR 7 +/// Minor version number. +# define PQXX_VERSION_MINOR 7 + +# define PQXX_VERSION_CHECK check_pqxx_version_7_7 + +namespace pqxx::internal +{ +/// Library version check stub. +/** Helps detect version mismatches between libpqxx headers and the libpqxx + * library binary. + * + * Sometimes users run into trouble linking their code against libpqxx because + * they build their own libpqxx, but the system also has a different version + * installed. The declarations in the headers against which they compile their + * code will differ from the ones used to build the libpqxx version they're + * using, leading to confusing link errors. The solution is to generate a link + * error when the libpqxx binary is not the same version as the libpqxx headers + * used to compile the code. + * + * This function's definition is in the libpqxx binary, so it's based on the + * version as found in the binary. The headers contain a call to the function, + * whose name contains the libpqxx version as found in the headers. (The + * library build process will use its own local headers even if another version + * of the headers is installed on the system.) + * + * If the libpqxx binary was compiled for a different version than the user's + * code, linking will fail with an error: `check_pqxx_version_*_*` will not + * exist for the given version number. + */ +PQXX_LIBEXPORT int PQXX_VERSION_CHECK() noexcept; +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/version.hxx.template b/ext/libpqxx-7.7.3/include/pqxx/version.hxx.template new file mode 100644 index 000000000..40837f16a --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/version.hxx.template @@ -0,0 +1,55 @@ +/* Version info for libpqxx. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/version instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_VERSION + +#if !defined(PQXX_HEADER_PRE) +#error "Include libpqxx headers as , not ." +#endif + +/// Full libpqxx version string. +# define PQXX_VERSION "@PQXXVERSION@" +/// Library ABI version. +# define PQXX_ABI "@PQXX_ABI@" + +/// Major version number. +# define PQXX_VERSION_MAJOR @PQXX_MAJOR@ +/// Minor version number. +# define PQXX_VERSION_MINOR @PQXX_MINOR@ + +# define PQXX_VERSION_CHECK check_pqxx_version_@PQXX_MAJOR@_@PQXX_MINOR@ + +namespace pqxx::internal +{ +/// Library version check stub. +/** Helps detect version mismatches between libpqxx headers and the libpqxx + * library binary. + * + * Sometimes users run into trouble linking their code against libpqxx because + * they build their own libpqxx, but the system also has a different version + * installed. The declarations in the headers against which they compile their + * code will differ from the ones used to build the libpqxx version they're + * using, leading to confusing link errors. The solution is to generate a link + * error when the libpqxx binary is not the same version as the libpqxx headers + * used to compile the code. + * + * This function's definition is in the libpqxx binary, so it's based on the + * version as found in the binary. The headers contain a call to the function, + * whose name contains the libpqxx version as found in the headers. (The + * library build process will use its own local headers even if another version + * of the headers is installed on the system.) + * + * If the libpqxx binary was compiled for a different version than the user's + * code, linking will fail with an error: `check_pqxx_version_*_*` will not + * exist for the given version number. + */ +PQXX_LIBEXPORT int PQXX_VERSION_CHECK() noexcept; +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/include/pqxx/zview b/ext/libpqxx-7.7.3/include/pqxx/zview new file mode 100644 index 000000000..66ea2a625 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/zview @@ -0,0 +1,6 @@ +/** Zero-terminated string view class. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/zview.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/include/pqxx/zview.hxx b/ext/libpqxx-7.7.3/include/pqxx/zview.hxx new file mode 100644 index 000000000..36a779f51 --- /dev/null +++ b/ext/libpqxx-7.7.3/include/pqxx/zview.hxx @@ -0,0 +1,163 @@ +/* Zero-terminated string view. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/zview instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ZVIEW +#define PQXX_H_ZVIEW + +#include +#include +#include + +#include "pqxx/types.hxx" + + +namespace pqxx +{ +/// Marker-type wrapper: zero-terminated `std::string_view`. +/** @warning Use this only if the underlying string is zero-terminated. + * + * When you construct a zview, you are promising that if the data pointer is + * non-null, the underlying string is zero-terminated. It otherwise behaves + * exactly like a std::string_view. + * + * The terminating zero is not "in" the string, so it does not count as part of + * the view's length. + * + * The added guarantee lets the view be used as a C-style string, which often + * matters since libpqxx builds on top of a C library. For this reason, zview + * also adds a @ref c_str method. + */ +class zview : public std::string_view +{ +public: + constexpr zview() noexcept = default; + + /// Convenience overload: construct using pointer and signed length. + constexpr zview(char const text[], std::ptrdiff_t len) : + std::string_view{text, static_cast(len)} + {} + + /// Convenience overload: construct using pointer and signed length. + constexpr zview(char text[], std::ptrdiff_t len) : + std::string_view{text, static_cast(len)} + {} + + /// Explicitly promote a `string_view` to a `zview`. + explicit constexpr zview(std::string_view other) noexcept : + std::string_view{other} + {} + + /// Construct from any initialiser you might use for `std::string_view`. + /** @warning Only do this if you are sure that the string is zero-terminated. + */ + template + explicit constexpr zview(Args &&...args) : + std::string_view(std::forward(args)...) + {} + + // C++20: constexpr. + /// @warning There's an implicit conversion from `std::string`. + zview(std::string const &str) noexcept : + std::string_view{str.c_str(), str.size()} + {} + + /// Construct a `zview` from a C-style string. + /** @warning This scans the string to discover its length. So if you need to + * do it many times, it's probably better to create the `zview` once and + * re-use it. + */ + constexpr zview(char const str[]) : std::string_view{str} {} + + /// Construct a `zview` from a string literal. + /** A C++ string literal ("foo") normally looks a lot like a pointer to + * char const, but that's not really true. It's actually an array of char, + * which _devolves_ to a pointer when you pass it. + * + * For the purpose of creating a `zview` there is one big difference: if we + * know the array's size, we don't need to scan through the string in order + * to find out its length. + */ + template + constexpr zview(char const (&literal)[size]) : zview(literal, size - 1) + {} + + /// Either a null pointer, or a zero-terminated text buffer. + [[nodiscard]] constexpr char const *c_str() const &noexcept + { + return data(); + } +}; + + +/// Support @ref zview literals. +/** You can "import" this selectively into your namespace, without pulling in + * all of the @ref pqxx namespace: + * + * ```cxx + * using pqxx::operator"" _zv; + * ``` + */ +constexpr zview operator"" _zv(char const str[], std::size_t len) noexcept +{ + return zview{str, len}; +} +} // namespace pqxx + + +#if defined(PQXX_HAVE_CONCEPTS) +/// A zview is a view. +template<> inline constexpr bool std::ranges::enable_view{true}; + + +/// A zview is a borrowed range. +template<> +inline constexpr bool std::ranges::enable_borrowed_range{true}; + +namespace pqxx::internal +{ +/// Concept: T is a known zero-terminated string type. +/** There's no unified API for these string types. It's just a check for some + * known types. Any code that makes use of the concept will still have to + * support each of these individually. + */ +template +concept ZString = std::is_convertible_v < strip_t, +char const * > or std::is_convertible_v, zview> or + std::is_convertible_v; +} // namespace pqxx::internal +#endif // PQXX_HAVE_CONCEPTS + + +namespace pqxx::internal +{ +/// Get a raw C string pointer. +inline constexpr char const *as_c_string(char const str[]) noexcept +{ + return str; +} +/// Get a raw C string pointer. +template +inline constexpr char const *as_c_string(char (&str)[N]) noexcept +{ + return str; +} +/// Get a raw C string pointer. +inline constexpr char const *as_c_string(pqxx::zview str) noexcept +{ + return str.c_str(); +} +// C++20: Make this constexpr. +/// Get a raw C string pointer. +inline char const *as_c_string(std::string const &str) noexcept +{ + return str.c_str(); +} +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/array b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/array new file mode 100644 index 000000000..689f5b27b --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/array @@ -0,0 +1,6 @@ +/** Handling of SQL arrays. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/array.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/array.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/array.hxx new file mode 100644 index 000000000..8440a244f --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/array.hxx @@ -0,0 +1,103 @@ +/* Handling of SQL arrays. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/field instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ARRAY +#define PQXX_H_ARRAY + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include + +#include "pqxx/internal/encoding_group.hxx" +#include "pqxx/internal/encodings.hxx" + + +namespace pqxx +{ +/// Low-level array parser. +/** Use this to read an array field retrieved from the database. + * + * This parser will only work reliably if your client encoding is UTF-8, ASCII, + * or a single-byte encoding which is a superset of ASCII (such as Latin-1). + * + * Also, the parser only supports array element types which use either a comma + * or a semicolon ("," or ";") as the separator between array elements. All + * built-in types use comma, except for one which uses semicolon, but some + * custom types may not work. + * + * The input is a C-style string containing the textual representation of an + * array, as returned by the database. The parser reads this representation + * on the fly. The string must remain in memory until parsing is done. + * + * Parse the array by making calls to @ref get_next until it returns a + * @ref juncture of "done". The @ref juncture tells you what the parser found + * in that step: did the array "nest" to a deeper level, or "un-nest" back up? + */ +class PQXX_LIBEXPORT array_parser +{ +public: + /// What's the latest thing found in the array? + enum class juncture + { + /// Starting a new row. + row_start, + /// Ending the current row. + row_end, + /// Found a NULL value. + null_value, + /// Found a string value. + string_value, + /// Parsing has completed. + done, + }; + + // TODO: constexpr noexcept. Breaks ABI. + /// Constructor. You don't need this; use @ref field::as_array instead. + /** The parser only remains valid while the data underlying the @ref result + * remains valid. Once all `result` objects referring to that data have been + * destroyed, the parser will no longer refer to valid memory. + */ + explicit array_parser( + std::string_view input, + internal::encoding_group = internal::encoding_group::MONOBYTE); + + /// Parse the next step in the array. + /** Returns what it found. If the juncture is @ref juncture::string_value, + * the string will contain the value. Otherwise, it will be empty. + * + * Call this until the @ref array_parser::juncture it returns is + * @ref juncture::done. + */ + std::pair get_next(); + +private: + std::string_view m_input; + internal::glyph_scanner_func *const m_scan; + + /// Current parsing position in the input. + std::string::size_type m_pos = 0u; + + std::string::size_type scan_single_quoted_string() const; + std::string parse_single_quoted_string(std::string::size_type end) const; + std::string::size_type scan_double_quoted_string() const; + std::string parse_double_quoted_string(std::string::size_type end) const; + std::string::size_type scan_unquoted_string() const; + std::string parse_unquoted_string(std::string::size_type end) const; + + std::string::size_type scan_glyph(std::string::size_type pos) const; + std::string::size_type + scan_glyph(std::string::size_type pos, std::string::size_type end) const; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/binarystring b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/binarystring new file mode 100644 index 000000000..77551d9f7 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/binarystring @@ -0,0 +1,6 @@ +/** BYTEA (binary string) conversions. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/binarystring.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/binarystring.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/binarystring.hxx new file mode 100644 index 000000000..47c82a035 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/binarystring.hxx @@ -0,0 +1,236 @@ +/* Deprecated representation for raw, binary data. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/binarystring instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_BINARYSTRING +#define PQXX_H_BINARYSTRING + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include + +#include "pqxx/result.hxx" +#include "pqxx/strconv.hxx" + +namespace pqxx +{ +class binarystring; +template<> struct string_traits; + + +/// Binary data corresponding to PostgreSQL's "BYTEA" binary-string type. +/** @ingroup escaping-functions + * @deprecated Use @c std::basic_string and + * @c std::basic_string_view for binary data. In C++20 or better, + * any @c contiguous_range of @c std::byte will do. + * + * This class represents a binary string as stored in a field of type @c bytea. + * + * Internally a binarystring is zero-terminated, but it may also contain null + * bytes, they're just like any other byte value. So don't assume that it's + * safe to treat the contents as a C-style string. + * + * The binarystring retains its value even if the result it was obtained from + * is destroyed, but it cannot be copied or assigned. + * + * \relatesalso transaction_base::quote_raw + * + * To include a @c binarystring value in an SQL query, escape and quote it + * using the transaction's @c quote_raw function. + * + * @warning This class is implemented as a reference-counting smart pointer. + * Copying, swapping, and destroying binarystring objects that refer to the + * same underlying data block is not thread-safe. If you wish to pass + * binarystrings around between threads, make sure that each of these + * operations is protected against concurrency with similar operations on the + * same object, or other objects pointing to the same data block. + */ +class PQXX_LIBEXPORT binarystring +{ +public: + using char_type = unsigned char; + using value_type = std::char_traits::char_type; + using size_type = std::size_t; + using difference_type = long; + using const_reference = value_type const &; + using const_pointer = value_type const *; + using const_iterator = const_pointer; + using const_reverse_iterator = std::reverse_iterator; + + [[deprecated("Use std::byte for binary data.")]] binarystring( + binarystring const &) = default; + + /// Read and unescape bytea field. + /** The field will be zero-terminated, even if the original bytea field + * isn't. + * @param F the field to read; must be a bytea field + */ + [[deprecated("Use std::byte for binary data.")]] explicit binarystring( + field const &); + + /// Copy binary data from std::string_view on binary data. + /** This is inefficient in that it copies the data to a buffer allocated on + * the heap. + */ + [[deprecated("Use std::byte for binary data.")]] explicit binarystring( + std::string_view); + + /// Copy binary data of given length straight out of memory. + [[deprecated("Use std::byte for binary data.")]] binarystring( + void const *, std::size_t); + + /// Efficiently wrap a buffer of binary data in a @c binarystring. + [[deprecated("Use std::byte for binary data.")]] binarystring( + std::shared_ptr ptr, size_type size) : + m_buf{std::move(ptr)}, m_size{size} + {} + + /// Size of converted string in bytes. + [[nodiscard]] size_type size() const noexcept { return m_size; } + /// Size of converted string in bytes. + [[nodiscard]] size_type length() const noexcept { return size(); } + [[nodiscard]] bool empty() const noexcept { return size() == 0; } + + [[nodiscard]] const_iterator begin() const noexcept { return data(); } + [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); } + [[nodiscard]] const_iterator end() const noexcept { return data() + m_size; } + [[nodiscard]] const_iterator cend() const noexcept { return end(); } + + [[nodiscard]] const_reference front() const noexcept { return *begin(); } + [[nodiscard]] const_reference back() const noexcept + { + return *(data() + m_size - 1); + } + + [[nodiscard]] const_reverse_iterator rbegin() const + { + return const_reverse_iterator{end()}; + } + [[nodiscard]] const_reverse_iterator crbegin() const { return rbegin(); } + [[nodiscard]] const_reverse_iterator rend() const + { + return const_reverse_iterator{begin()}; + } + [[nodiscard]] const_reverse_iterator crend() const { return rend(); } + + /// Unescaped field contents. + [[nodiscard]] value_type const *data() const noexcept { return m_buf.get(); } + + [[nodiscard]] const_reference operator[](size_type i) const noexcept + { + return data()[i]; + } + + [[nodiscard]] PQXX_PURE bool operator==(binarystring const &) const noexcept; + [[nodiscard]] bool operator!=(binarystring const &rhs) const noexcept + { + return not operator==(rhs); + } + + binarystring &operator=(binarystring const &); + + /// Index contained string, checking for valid index. + const_reference at(size_type) const; + + /// Swap contents with other binarystring. + void swap(binarystring &); + + /// Raw character buffer (no terminating zero is added). + /** @warning No terminating zero is added! If the binary data did not end in + * a null character, you will not find one here. + */ + [[nodiscard]] char const *get() const noexcept + { + return reinterpret_cast(m_buf.get()); + } + + /// Read contents as a std::string_view. + [[nodiscard]] std::string_view view() const noexcept + { + return std::string_view(get(), size()); + } + + /// Read as regular C++ string (may include null characters). + /** This creates and returns a new string object. Don't call this + * repeatedly; retrieve your string once and keep it in a local variable. + * Also, do not expect to be able to compare the string's address to that of + * an earlier invocation. + */ + [[nodiscard]] std::string str() const; + + /// Access data as a pointer to @c std::byte. + [[nodiscard]] std::byte const *bytes() const + { + return reinterpret_cast(get()); + } + + /// Read data as a @c std::basic_string_view. + [[nodiscard]] std::basic_string_view bytes_view() const + { + return std::basic_string_view{bytes(), size()}; + } + +private: + std::shared_ptr m_buf; + size_type m_size{0}; +}; + + +template<> struct nullness : no_null +{}; + + +/// String conversion traits for @c binarystring. +/** Defines the conversions between a @c binarystring and its PostgreSQL + * textual format, for communication with the database. + * + * These conversions rely on the "hex" format which was introduced in + * PostgreSQL 9.0. Both your libpq and the server must be recent enough to + * speak this format. + */ +template<> struct string_traits +{ + static std::size_t size_buffer(binarystring const &value) noexcept + { + return internal::size_esc_bin(std::size(value)); + } + + static zview to_buf(char *begin, char *end, binarystring const &value) + { + return generic_to_buf(begin, end, value); + } + + static char *into_buf(char *begin, char *end, binarystring const &value) + { + auto const budget{size_buffer(value)}; + if (internal::cmp_less(end - begin, budget)) + throw conversion_overrun{ + "Not enough buffer space to escape binary data."}; + std::string_view text{value.view()}; + internal::esc_bin(binary_cast(text), begin); + return begin + budget; + } + + static binarystring from_string(std::string_view text) + { + auto const size{pqxx::internal::size_unesc_bin(std::size(text))}; + std::shared_ptr buf{ + new unsigned char[size], [](unsigned char const *x) { delete[] x; }}; + pqxx::internal::unesc_bin(text, reinterpret_cast(buf.get())); +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return binarystring{std::move(buf), size}; +#include "pqxx/internal/ignore-deprecated-post.hxx" + } +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/blob b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/blob new file mode 100644 index 000000000..3fd0afac9 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/blob @@ -0,0 +1,6 @@ +/** Binary Large Objects interface. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/blob.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/blob.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/blob.hxx new file mode 100644 index 000000000..6d77be724 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/blob.hxx @@ -0,0 +1,351 @@ +/* Binary Large Objects interface. + * + * Read or write large objects, stored in their own storage on the server. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/largeobject instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_BLOB +#define PQXX_H_BLOB + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#if defined(PQXX_HAVE_PATH) +# include +#endif + +#if defined(PQXX_HAVE_RANGES) && __has_include() +# include +#endif + +#if defined(PQXX_HAVE_SPAN) && __has_include() +# include +#endif + +#include "pqxx/dbtransaction.hxx" + + +namespace pqxx +{ +/** Binary large object. + * + * This is how you store data that may be too large for the `BYTEA` type. + * Access operations are similar to those for a file: you can read, write, + * query or set the current reading/writing position, and so on. + * + * These large objects live in their own storage on the server, indexed by an + * integer object identifier ("oid"). + * + * Two `blob` objects may refer to the same actual large object in the + * database at the same time. Each will have its own reading/writing position, + * but writes to the one will of course affect what the other sees. + */ +class PQXX_LIBEXPORT blob +{ +public: + /// Create a new, empty large object. + /** You may optionally specify an oid for the new blob. If you do, then + * the new object will have that oid -- or creation will fail if there + * already is an object with that oid. + */ + [[nodiscard]] static oid create(dbtransaction &, oid = 0); + + /// Delete a large object, or fail if it does not exist. + static void remove(dbtransaction &, oid); + + /// Open blob for reading. Any attempt to write to it will fail. + [[nodiscard]] static blob open_r(dbtransaction &, oid); + // Open blob for writing. Any attempt to read from it will fail. + [[nodiscard]] static blob open_w(dbtransaction &, oid); + // Open blob for reading and/or writing. + [[nodiscard]] static blob open_rw(dbtransaction &, oid); + + /// You can default-construct a blob, but it won't do anything useful. + /** Most operations on a default-constructed blob will throw @ref + * usage_error. + */ + blob() = default; + + /// You can move a blob, but not copy it. The original becomes unusable. + blob(blob &&); + /// You can move a blob, but not copy it. The original becomes unusable. + blob &operator=(blob &&); + + blob(blob const &) = delete; + blob &operator=(blob const &) = delete; + ~blob(); + + /// Maximum number of bytes that can be read or written at a time. + /** The underlying protocol only supports reads and writes up to 2 GB + * exclusive. + * + * If you need to read or write more data to or from a binary large object, + * you'll have to break it up into chunks. + */ + static constexpr std::size_t chunk_limit = 0x7fffffff; + + /// Read up to `size` bytes of the object into `buf`. + /** Uses a buffer that you provide, resizing it as needed. If it suits you, + * this lets you allocate the buffer once and then re-use it multiple times. + * + * Resizes `buf` as needed. + * + * @warning The underlying protocol only supports reads up to 2GB at a time. + * If you need to read more, try making repeated calls to @ref append_to_buf. + */ + std::size_t read(std::basic_string &buf, std::size_t size); + +#if defined(PQXX_HAVE_SPAN) + /// Read up to `std::size(buf)` bytes from the object. + /** Retrieves bytes from the blob, at the current position, until `buf` is + * full or there are no more bytes to read, whichever comes first. + * + * Returns the filled portion of `buf`. This may be empty. + */ + template + std::span read(std::span buf) + { + return buf.subspan(0, raw_read(std::data(buf), std::size(buf))); + } +#endif // PQXX_HAVE_SPAN + +#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) + /// Read up to `std::size(buf)` bytes from the object. + /** Retrieves bytes from the blob, at the current position, until `buf` is + * full or there are no more bytes to read, whichever comes first. + * + * Returns the filled portion of `buf`. This may be empty. + */ + template std::span read(DATA &buf) + { + return {std::data(buf), raw_read(std::data(buf), std::size(buf))}; + } +#else // PQXX_HAVE_CONCEPTS && PQXX_HAVE_SPAN + /// Read up to `std::size(buf)` bytes from the object. + /** @deprecated As libpqxx moves to C++20 as its baseline language version, + * this will take and return `std::span`. + * + * Retrieves bytes from the blob, at the current position, until `buf` is + * full (i.e. its current size is reached), or there are no more bytes to + * read, whichever comes first. + * + * This function will not change either the size or the capacity of `buf`, + * only its contents. + * + * Returns the filled portion of `buf`. This may be empty. + */ + template + std::basic_string_view read(std::vector &buf) + { + return {std::data(buf), raw_read(std::data(buf), std::size(buf))}; + } +#endif // PQXX_HAVE_CONCEPTS && PQXX_HAVE_SPAN + +#if defined(PQXX_HAVE_CONCEPTS) + /// Write `data` to large object, at the current position. + /** If the writing position is at the end of the object, this will append + * `data` to the object's contents and move the writing position so that + * it's still at the end. + * + * If the writing position was not at the end, writing will overwrite the + * prior data, but it will not remove data that follows the part where you + * wrote your new data. + * + * @warning This is a big difference from writing to a file. You can + * overwrite some data in a large object, but this does not truncate the + * data that was already there. For example, if the object contained binary + * data "abc", and you write "12" at the starting position, the object will + * contain "12c". + * + * @warning The underlying protocol only supports writes up to 2 GB at a + * time. If you need to write more, try making repeated calls to + * @ref append_from_buf. + */ + template void write(DATA const &data) + { + raw_write(std::data(data), std::size(data)); + } +#else + /// Write `data` large object, at the current position. + /** If the writing position is at the end of the object, this will append + * `data` to the object's contents and move the writing position so that + * it's still at the end. + * + * If the writing position was not at the end, writing will overwrite the + * prior data, but it will not remove data that follows the part where you + * wrote your new data. + * + * @warning This is a big difference from writing to a file. You can + * overwrite some data in a large object, but this does not truncate the + * data that was already there. For example, if the object contained binary + * data "abc", and you write "12" at the starting position, the object will + * contain "12c". + * + * @warning The underlying protocol only supports writes up to 2 GB at a + * time. If you need to write more, try making repeated calls to + * @ref append_from_buf. + */ + template void write(DATA const &data) + { + raw_write(std::data(data), std::size(data)); + } +#endif + + /// Resize large object to `size` bytes. + /** If the blob is more than `size` bytes long, this removes the end so as + * to make the blob the desired length. + * + * If the blob is less than `size` bytes long, it adds enough zero bytes to + * make it the desired length. + */ + void resize(std::int64_t size); + + /// Return the current reading/writing position in the large object. + [[nodiscard]] std::int64_t tell() const; + + /// Set the current reading/writing position to an absolute offset. + /** Returns the new file offset. */ + std::int64_t seek_abs(std::int64_t offset = 0); + /// Move the current reading/writing position forwards by an offset. + /** To move backwards, pass a negative offset. + * + * Returns the new file offset. + */ + std::int64_t seek_rel(std::int64_t offset = 0); + /// Set the current position to an offset relative to the end of the blob. + /** You'll probably want an offset of zero or less. + * + * Returns the new file offset. + */ + std::int64_t seek_end(std::int64_t offset = 0); + + /// Create a binary large object containing given `data`. + /** You may optionally specify an oid for the new object. If you do, and an + * object with that oid already exists, creation will fail. + */ + static oid from_buf( + dbtransaction &tx, std::basic_string_view data, oid id = 0); + + /// Append `data` to binary large object. + /** The underlying protocol only supports appending blocks up to 2 GB. + */ + static void append_from_buf( + dbtransaction &tx, std::basic_string_view data, oid id); + + /// Read client-side file and store it server-side as a binary large object. + [[nodiscard]] static oid from_file(dbtransaction &, char const path[]); + +#if defined(PQXX_HAVE_PATH) && !defined(_WIN32) + /// Read client-side file and store it server-side as a binary large object. + /** This overload is not available on Windows, where `std::filesystem::path` + * converts to a `wchar_t` string rather than a `char` string. + */ + [[nodiscard]] static oid + from_file(dbtransaction &tx, std::filesystem::path const &path) + { + return from_file(tx, path.c_str()); + } +#endif + + /// Read client-side file and store it server-side as a binary large object. + /** In this version, you specify the binary large object's oid. If that oid + * is already in use, the operation will fail. + */ + static oid from_file(dbtransaction &, char const path[], oid); + +#if defined(PQXX_HAVE_PATH) && !defined(_WIN32) + /// Read client-side file and store it server-side as a binary large object. + /** In this version, you specify the binary large object's oid. If that oid + * is already in use, the operation will fail. + * + * This overload is not available on Windows, where `std::filesystem::path` + * converts to a `wchar_t` string rather than a `char` string. + */ + static oid + from_file(dbtransaction &tx, std::filesystem::path const &path, oid id) + { + return from_file(tx, path.c_str(), id); + } +#endif + + /// Convenience function: Read up to `max_size` bytes from blob with `id`. + /** You could easily do this yourself using the @ref open_r and @ref read + * functions, but it can save you a bit of code to do it this way. + */ + static void to_buf( + dbtransaction &, oid, std::basic_string &, + std::size_t max_size); + + /// Read part of the binary large object with `id`, and append it to `buf`. + /** Use this to break up a large read from one binary large object into one + * massive buffer. Just keep calling this function until it returns zero. + * + * The `offset` is how far into the large object your desired chunk is, and + * `append_max` says how much to try and read in one go. + */ + static std::size_t append_to_buf( + dbtransaction &tx, oid id, std::int64_t offset, + std::basic_string &buf, std::size_t append_max); + + /// Write a binary large object's contents to a client-side file. + static void to_file(dbtransaction &, oid, char const path[]); + +#if defined(PQXX_HAVE_PATH) && !defined(_WIN32) + /// Write a binary large object's contents to a client-side file. + /** This overload is not available on Windows, where `std::filesystem::path` + * converts to a `wchar_t` string rather than a `char` string. + */ + static void + to_file(dbtransaction &tx, oid id, std::filesystem::path const &path) + { + to_file(tx, id, path.c_str()); + } +#endif + + /// Close this blob. + /** This does not delete the blob from the database; it only terminates your + * local object for accessing the blob. + * + * Resets the blob to a useless state similar to one that was + * default-constructed. + * + * The destructor will do this for you automatically. Still, there is a + * reason to `close()` objects explicitly where possible: if an error should + * occur while closing, `close()` can throw an exception. A destructor + * cannot. + */ + void close(); + +private: + PQXX_PRIVATE blob(connection &conn, int fd) noexcept : + m_conn{&conn}, m_fd{fd} + {} + static PQXX_PRIVATE blob open_internal(dbtransaction &, oid, int); + static PQXX_PRIVATE pqxx::internal::pq::PGconn * + raw_conn(pqxx::connection *) noexcept; + static PQXX_PRIVATE pqxx::internal::pq::PGconn * + raw_conn(pqxx::dbtransaction const &) noexcept; + static PQXX_PRIVATE std::string errmsg(connection const *); + static PQXX_PRIVATE std::string errmsg(dbtransaction const &tx) + { + return errmsg(&tx.conn()); + } + PQXX_PRIVATE std::string errmsg() const { return errmsg(m_conn); } + PQXX_PRIVATE std::int64_t seek(std::int64_t offset, int whence); + std::size_t raw_read(std::byte buf[], std::size_t size); + void raw_write(std::byte const buf[], std::size_t size); + + connection *m_conn = nullptr; + int m_fd = -1; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/composite b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/composite new file mode 100644 index 000000000..2bfa7ade9 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/composite @@ -0,0 +1,6 @@ +/** Handling of SQL "composite types." + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/composite.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/composite.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/composite.hxx new file mode 100644 index 000000000..439b133a8 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/composite.hxx @@ -0,0 +1,149 @@ +#ifndef PQXX_H_COMPOSITE +#define PQXX_H_COMPOSITE + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/internal/array-composite.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/util.hxx" + +namespace pqxx +{ +/// Parse a string representation of a value of a composite type. +/** @warning This code is still experimental. Use with care. + * + * You may use this as a helper while implementing your own @ref string_traits + * for a composite type. + * + * This function interprets `text` as the string representation of a value of + * some composite type, and sets each of `fields` to the respective values of + * its fields. The field types must be copy-assignable. + * + * The number of fields must match the number of fields in the composite type, + * and there must not be any other text in the input. The function is meant to + * handle any value string that the backend can produce, but not necessarily + * every valid alternative spelling. + * + * Fields in composite types can be null. When this happens, the C++ type of + * the corresponding field reference must be of a type that can handle nulls. + * If you are working with a type that does not have an inherent null value, + * such as e.g. `int`, consider using `std::optional`. + */ +template +inline void parse_composite( + pqxx::internal::encoding_group enc, std::string_view text, T &...fields) +{ + static_assert(sizeof...(fields) > 0); + + auto const scan{pqxx::internal::get_glyph_scanner(enc)}; + auto const data{std::data(text)}; + auto const size{std::size(text)}; + if (size == 0) + throw conversion_error{"Cannot parse composite value from empty string."}; + + std::size_t here{0}, next{scan(data, size, here)}; + if (next != 1 or data[here] != '(') + throw conversion_error{ + internal::concat("Invalid composite value string: ", text)}; + + here = next; + + constexpr auto num_fields{sizeof...(fields)}; + std::size_t index{0}; + (pqxx::internal::parse_composite_field( + index, text, here, fields, scan, num_fields - 1), + ...); + if (here != std::size(text)) + throw conversion_error{internal::concat( + "Composite value did not end at the closing parenthesis: '", text, + "'.")}; + if (text[here - 1] != ')') + throw conversion_error{internal::concat( + "Composive value did not end in parenthesis: '", text, "'")}; +} + + +/// Parse a string representation of a value of a composite type. +/** @warning This version only works for UTF-8 and single-byte encodings. + * + * For proper encoding support, use the composite-type support in the + * `field` class. + */ +template +inline void parse_composite(std::string_view text, T &...fields) +{ + parse_composite(pqxx::internal::encoding_group::MONOBYTE, text, fields...); +} +} // namespace pqxx + + +namespace pqxx::internal +{ +constexpr char empty_composite_str[]{"()"}; +} // namespace pqxx::internal + + +namespace pqxx +{ +/// Estimate the buffer size needed to represent a value of a composite type. +/** Returns a conservative estimate. + */ +template +[[nodiscard]] inline std::size_t +composite_size_buffer(T const &...fields) noexcept +{ + constexpr auto num{sizeof...(fields)}; + + // Size for a multi-field composite includes room for... + // + opening parenthesis + // + field budgets + // + separating comma per field + // - comma after final field + // + closing parenthesis + // + terminating zero + + if constexpr (num == 0) + return std::size(pqxx::internal::empty_composite_str); + else + return 1 + (pqxx::internal::size_composite_field_buffer(fields) + ...) + + num + 1; +} + + +/// Render a series of values as a single composite SQL value. +/** @warning This code is still experimental. Use with care. + * + * You may use this as a helper while implementing your own `string_traits` + * for a composite type. + */ +template +inline char *composite_into_buf(char *begin, char *end, T const &...fields) +{ + if (std::size_t(end - begin) < composite_size_buffer(fields...)) + throw conversion_error{ + "Buffer space may not be enough to represent composite value."}; + + constexpr auto num_fields{sizeof...(fields)}; + if constexpr (num_fields == 0) + { + constexpr char empty[]{"()"}; + std::memcpy(begin, empty, std::size(empty)); + return begin + std::size(empty); + } + + char *pos{begin}; + *pos++ = '('; + + (pqxx::internal::write_composite_field(pos, end, fields), ...); + + // If we've got multiple fields, "backspace" that last comma. + if constexpr (num_fields > 1) + --pos; + *pos++ = ')'; + *pos++ = '\0'; + return pos; +} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/config-public-compiler.h b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/config-public-compiler.h new file mode 100644 index 000000000..3668a10f8 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/config-public-compiler.h @@ -0,0 +1,81 @@ +/* include/pqxx/config.h.in. Generated from configure.ac by autoheader. */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_INTTYPES_H */ +/* Define to 1 if you have the `pq' library (-lpq). */ +/* #undef HAVE_LIBPQ */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MEMORY_H */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STDINT_H */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STDLIB_H */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STRINGS_H */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STRING_H */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_STAT_H */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_TYPES_H */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UNISTD_H */ +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +/* #undef LT_OBJDIR */ +/* Name of package */ +/* #undef PACKAGE */ +/* Define to the address where bug reports for this package should be sent. */ +/* #undef PACKAGE_BUGREPORT */ +/* Define to the full name of this package. */ +/* #undef PACKAGE_NAME */ +/* Define to the full name and version of this package. */ +/* #undef PACKAGE_STRING */ +/* Define to the one symbol short name of this package. */ +/* #undef PACKAGE_TARNAME */ +/* Define to the home page for this package. */ +/* #undef PACKAGE_URL */ +/* Define to the version of this package. */ +/* #undef PACKAGE_VERSION */ +/* Define if supports floating-point conversion. */ +#define PQXX_HAVE_CHARCONV_FLOAT +/* Define if supports integer conversion. */ +#define PQXX_HAVE_CHARCONV_INT +/* Define if compiler has C++20 std::cmp_greater etc. */ +/* #undef PQXX_HAVE_CMP */ +/* Define if compiler supports Concepts and header. */ +/* #undef PQXX_HAVE_CONCEPTS */ +/* Define if compiler supports __cxa_demangle */ +#define PQXX_HAVE_CXA_DEMANGLE +/* Define if g++ supports pure attribute */ +#define PQXX_HAVE_GCC_PURE +/* Define if g++ supports visibility attribute. */ +#define PQXX_HAVE_GCC_VISIBILITY +/* Define if likely & unlikely work. */ +/* #undef PQXX_HAVE_LIKELY */ +/* Define if operator[] can take multiple arguments. */ +/* #undef PQXX_HAVE_MULTIDIMENSIONAL_SUBSCRIPT */ +/* Define if compiler has usable std::filesystem::path. */ +#define PQXX_HAVE_PATH +/* Define if poll() is available. */ +#define PQXX_HAVE_POLL +/* Define if libpq has PQencryptPasswordConn (since pg 10). */ +#define PQXX_HAVE_PQENCRYPTPASSWORDCONN +/* Define if libpq has pipeline mode (since pg 14). */ +#define PQXX_HAVE_PQ_PIPELINE +/* Define if std::this_thread::sleep_for works. */ +#define PQXX_HAVE_SLEEP_FOR +/* Define if compiler has std::span. */ +/* #undef PQXX_HAVE_SPAN */ +/* Define if strerror_r() is available. */ +#define PQXX_HAVE_STRERROR_R +/* Define if strerror_s() is available. */ +/* #undef PQXX_HAVE_STRERROR_S */ +/* Define if thread_local is fully supported. */ +#define PQXX_HAVE_THREAD_LOCAL +/* Define if std::chrono has year_month_day etc. */ +/* #undef PQXX_HAVE_YEAR_MONTH_DAY */ +/* Define to 1 if you have the ANSI C header files. */ +/* #undef STDC_HEADERS */ +/* Version number of package */ +/* #undef VERSION */ diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/connection b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/connection new file mode 100644 index 000000000..82ff43aa5 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/connection @@ -0,0 +1,8 @@ +/** pqxx::connection class. + * + * pqxx::connection encapsulates a connection to a database. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/connection.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/connection.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/connection.hxx new file mode 100644 index 000000000..92454bb47 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/connection.hxx @@ -0,0 +1,1261 @@ +/* Definition of the connection class. + * + * pqxx::connection encapsulates a connection to a database. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/connection instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_CONNECTION +#define PQXX_H_CONNECTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Double-check in order to suppress an overzealous Visual C++ warning (#418). +#if defined(PQXX_HAVE_CONCEPTS) && __has_include() +# include +#endif + +#include "pqxx/errorhandler.hxx" +#include "pqxx/except.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/params.hxx" +#include "pqxx/separated_list.hxx" +#include "pqxx/strconv.hxx" +#include "pqxx/types.hxx" +#include "pqxx/util.hxx" +#include "pqxx/zview.hxx" + + +/** + * @addtogroup connections + * + * Use of the libpqxx library starts here. + * + * Everything that can be done with a database through libpqxx must go through + * a @ref pqxx::connection object. It connects to a database when you create + * it, and it terminates that communication during destruction. + * + * Many things come together in this class. Handling of error and warning + * messages, for example, is defined by @ref pqxx::errorhandler objects in the + * context of a connection. Prepared statements are also defined here. + * + * When you connect to a database, you pass a connection string containing any + * parameters and options, such as the server address and the database name. + * + * These are identical to the ones in libpq, the C language binding upon which + * libpqxx itself is built: + * + * https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING + * + * There are also environment variables you can set to provide defaults, again + * as defined by libpq: + * + * https://www.postgresql.org/docs/current/libpq-envars.html + * + * You can also create a database connection _asynchronously_ using an + * intermediate @ref pqxx::connecting object. + */ + +namespace pqxx::internal +{ +class sql_cursor; + +#if defined(PQXX_HAVE_CONCEPTS) +/// Concept: T is a range of pairs of zero-terminated strings. +template +concept ZKey_ZValues = std::ranges::input_range and requires(T t) +{ + {std::cbegin(t)}; + { + std::get<0>(*std::cbegin(t)) + } -> ZString; + { + std::get<1>(*std::cbegin(t)) + } -> ZString; +} and std::tuple_size_v::value_type> +== 2; +#endif // PQXX_HAVE_CONCEPTS +} // namespace pqxx::internal + + +namespace pqxx::internal::gate +{ +class connection_dbtransaction; +class connection_errorhandler; +class connection_largeobject; +class connection_notification_receiver; +class connection_pipeline; +class connection_sql_cursor; +class connection_stream_from; +class connection_stream_to; +class connection_transaction; +class const_connection_largeobject; +} // namespace pqxx::internal::gate + + +namespace pqxx +{ +/// Representation of a PostgreSQL table path. +/** A "table path" consists of a table name, optionally prefixed by a schema + * name, which in turn is optionally prefixed by a database name. + * + * A minimal example of a table path would be `{mytable}`. But a table path + * may also take the forms `{myschema,mytable}` or + * `{mydb,myschema,mytable}`. + */ +using table_path = std::initializer_list; + + +/// Encrypt a password. @deprecated Use connection::encrypt_password instead. +[[nodiscard, + deprecated("Use connection::encrypt_password instead.")]] std::string + PQXX_LIBEXPORT + encrypt_password(char const user[], char const password[]); + +/// Encrypt password. @deprecated Use connection::encrypt_password instead. +[[nodiscard, + deprecated("Use connection::encrypt_password instead.")]] inline std::string +encrypt_password(zview user, zview password) +{ +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return encrypt_password(user.c_str(), password.c_str()); +#include "pqxx/internal/ignore-deprecated-post.hxx" +} + + +/// Error verbosity levels. +enum class error_verbosity : int +{ + // These values must match those in libpq's PGVerbosity enum. + terse = 0, + normal = 1, + verbose = 2 +}; + + +/// Connection to a database. +/** This is the first class to look at when you wish to work with a database + * through libpqxx. The connection opens during construction, and closes upon + * destruction. + * + * When creating a connection, you can pass a connection URI or a postgres + * connection string, to specify the database server's address, a login + * username, and so on. If you don't, the connection will try to obtain them + * from certain environment variables. If those are not set either, the + * default is to try and connect to the local system's port 5432. + * + * Find more about connection strings here: + * + * https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING + * + * The variables are documented here: + * + * https://www.postgresql.org/docs/current/libpq-envars.html + * + * To query or manipulate the database once connected, use one of the + * transaction classes (see pqxx/transaction_base.hxx) and perhaps also the + * transactor framework (see pqxx/transactor.hxx). + * + * When a connection breaks, you will typically get a @ref broken_connection + * exception. This can happen at almost any point. + * + * @warning On Unix-like systems, including GNU and BSD systems, your program + * may receive the SIGPIPE signal when the connection to the backend breaks. By + * default this signal will abort your program. Use "signal(SIGPIPE, SIG_IGN)" + * if you want your program to continue running after a connection fails. + */ +class PQXX_LIBEXPORT connection +{ +public: + connection() : connection{""} {} + + /// Connect to a database, using `options` string. + explicit connection(char const options[]) + { + check_version(); + init(options); + } + + /// Connect to a database, using `options` string. + explicit connection(zview options) : connection{options.c_str()} + { + // (Delegates to other constructor which calls check_version for us.) + } + + /// Move constructor. + /** Moving a connection is not allowed if it has an open transaction, or has + * error handlers or notification receivers registered on it. In those + * situations, other objects may hold references to the old object which + * would become invalid and might produce hard-to-diagnose bugs. + */ + connection(connection &&rhs); + +#if defined(PQXX_HAVE_CONCEPTS) + /// Connect to a database, passing options as a range of key/value pairs. + /** @warning Experimental. Requires C++20 "concepts" support. Define + * `PQXX_HAVE_CONCEPTS` to enable it. + * + * There's no need to escape the parameter values. + * + * See the PostgreSQL libpq documentation for the full list of possible + * options: + * + * https://postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS + * + * The options can be anything that can be iterated as a series of pairs of + * zero-terminated strings: `std::pair`, or + * `std::tuple`, or + * `std::map`, and so on. + */ + template + inline connection(MAPPING const ¶ms); +#endif // PQXX_HAVE_CONCEPTS + + ~connection() + { + try + { + close(); + } + catch (std::exception const &) + {} + } + + /// Move assignment. + /** Neither connection can have an open transaction, registered error + * handlers, or registered notification receivers. + */ + connection &operator=(connection &&rhs); + + connection(connection const &) = delete; + connection &operator=(connection const &) = delete; + + /// Is this connection open at the moment? + /** @warning This function is **not** needed in most code. Resist the + * temptation to check it after opening a connection. The `connection` + * constructor will throw a @ref broken_connection exception if can't connect + * to the database. + */ + [[nodiscard]] bool PQXX_PURE is_open() const noexcept; + + /// Invoke notice processor function. The message should end in newline. + void process_notice(char const[]) noexcept; + /// Invoke notice processor function. Newline at end is recommended. + /** The zview variant, with a message ending in newline, is the most + * efficient way to call process_notice. + */ + void process_notice(zview) noexcept; + + /// Enable tracing to a given output stream, or nullptr to disable. + void trace(std::FILE *) noexcept; + + /** + * @name Connection properties + * + * These are probably not of great interest, since most are derived from + * information supplied by the client program itself, but they are included + * for completeness. + * + * The connection needs to be currently active for these to work. + */ + //@{ + /// Name of database we're connected to, if any. + [[nodiscard]] char const *dbname() const; + + /// Database user ID we're connected under, if any. + [[nodiscard]] char const *username() const; + + /// Address of server, or nullptr if none specified (i.e. default or local) + [[nodiscard]] char const *hostname() const; + + /// Server port number we're connected to. + [[nodiscard]] char const *port() const; + + /// Process ID for backend process, or 0 if inactive. + [[nodiscard]] int PQXX_PURE backendpid() const &noexcept; + + /// Socket currently used for connection, or -1 for none. Use with care! + /** Query the current socket number. This is intended for event loops based + * on functions such as select() or poll(), where you're waiting for any of + * multiple file descriptors to become ready for communication. + * + * Please try to stay away from this function. It is really only meant for + * event loops that need to wait on more than one file descriptor. If all + * you need is to block until a notification arrives, for instance, use + * await_notification(). If you want to issue queries and retrieve results + * in nonblocking fashion, check out the pipeline class. + */ + [[nodiscard]] int PQXX_PURE sock() const &noexcept; + + /// What version of the PostgreSQL protocol is this connection using? + /** The answer can be 0 (when there is no connection); 3 for protocol 3.0; or + * possibly higher values as newer protocol versions come into use. + */ + [[nodiscard]] int PQXX_PURE protocol_version() const noexcept; + + /// What version of the PostgreSQL server are we connected to? + /** The result is a bit complicated: each of the major, medium, and minor + * release numbers is written as a two-digit decimal number, and the three + * are then concatenated. Thus server version 9.4.2 will be returned as the + * decimal number 90402. If there is no connection to the server, this + * returns zero. + * + * @warning When writing version numbers in your code, don't add zero at the + * beginning! Numbers beginning with zero are interpreted as octal (base-8) + * in C++. Thus, 070402 is not the same as 70402, and 080000 is not a number + * at all because there is no digit "8" in octal notation. Use strictly + * decimal notation when it comes to these version numbers. + */ + [[nodiscard]] int PQXX_PURE server_version() const noexcept; + //@} + + /// @name Text encoding + /** + * Each connection is governed by a "client encoding," which dictates how + * strings and other text is represented in bytes. The database server will + * send text data to you in this encoding, and you should use it for the + * queries and data which you send to the server. + * + * Search the PostgreSQL documentation for "character set encodings" to find + * out more about the available encodings, how to extend them, and how to use + * them. Not all server-side encodings are compatible with all client-side + * encodings or vice versa. + * + * Encoding names are case-insensitive, so e.g. "UTF8" is equivalent to + * "utf8". + * + * You can change the client encoding, but this may not work when the + * connection is in a special state, such as when streaming a table. It's + * not clear what happens if you change the encoding during a transaction, + * and then abort the transaction. + */ + //@{ + /// Get client-side character encoding, by name. + [[nodiscard]] std::string get_client_encoding() const; + + /// Set client-side character encoding, by name. + /** + * @param encoding Name of the character set encoding to use. + */ + void set_client_encoding(zview encoding) & + { + set_client_encoding(encoding.c_str()); + } + + /// Set client-side character encoding, by name. + /** + * @param encoding Name of the character set encoding to use. + */ + void set_client_encoding(char const encoding[]) &; + + /// Get the connection's encoding, as a PostgreSQL-defined code. + [[nodiscard]] int PQXX_PRIVATE encoding_id() const; + + //@} + + /// Set session variable, using SQL's `SET` command. + /** @deprecated To set a session variable, use @ref set_session_var. To set + * a transaction-local variable, execute an SQL `SET` command. + * + * @warning When setting a string value, you must escape and quote it first. + * Use the @ref quote() function to do that. + * + * @warning This executes an SQL query, so do not get or set variables while + * a table stream or pipeline is active on the same connection. + * + * @param var Variable to set. + * @param value New value for Var. This can be any SQL expression. If it's + * a string, be sure that it's properly escaped and quoted. + */ + [[deprecated("To set session variables, use set_session_var.")]] void + set_variable(std::string_view var, std::string_view value) &; + + /// Set one of the session variables to a new value. + /** This executes SQL, so do not do it while a pipeline or stream is active + * on the connection. + * + * The value you set here will last for the rest of the connection's + * duration, or until you set a new value. + * + * If you set the value while in a @ref dbtransaction (i.e. any transaction + * that is not a @ref nontransaction), then rolling back the transaction will + * undo the change. + * + * All applies to setting _session_ variables. You can also set the same + * variables as _local_ variables, in which case they will always revert to + * their previous value when the transaction ends (or when you overwrite them + * of course). To set a local variable, simply execute an SQL statement + * along the lines of "`SET LOCAL var = 'value'`" inside your transaction. + * + * @param var The variable to set. + * @param value The new value for the variable. + * @throw @ref variable_set_to_null if the value is null; this is not + * allowed. + */ + template + void set_session_var(std::string_view var, TYPE const &value) & + { + if constexpr (nullness::has_null) + { + if (nullness::is_null(value)) + throw variable_set_to_null{ + internal::concat("Attempted to set variable ", var, " to null.")}; + } + exec(internal::concat("SET ", quote_name(var), "=", quote(value))); + } + + /// Read session variable, using SQL's `SHOW` command. + /** @warning This executes an SQL query, so do not get or set variables while + * a table stream or pipeline is active on the same connection. + */ + [[deprecated("Use get_var instead.")]] std::string + get_variable(std::string_view); + + /// Read currently applicable value of a variable. + /** This function executes an SQL statement, so it won't work while a + * @ref pipeline or query stream is active on the connection. + * + * @return a blank `std::optional` if the variable's value is null, or its + * string value otherwise. + */ + std::string get_var(std::string_view var); + + /// Read currently applicable value of a variable. + /** This function executes an SQL statement, so it won't work while a + * @ref pipeline or query stream is active on the connection. + * + * If there is any possibility that the variable is null, ensure that `TYPE` + * can represent null values. + */ + template TYPE get_var_as(std::string_view var) + { + return from_string(get_var(var)); + } + + /** + * @name Notifications and Receivers + */ + //@{ + /// Check for pending notifications and take appropriate action. + /** This does not block. To wait for incoming notifications, either call + * await_notification() (it calls this function); or wait for incoming data + * on the connection's socket (i.e. wait to read), and then call this + * function repeatedly until it returns zero. After that, there are no more + * pending notifications so you may want to wait again. + * + * If any notifications are pending when you call this function, it + * processes them by finding any receivers that match the notification string + * and invoking those. If no receivers match, there is nothing to invoke but + * we do consider the notification processed. + * + * If any of the client-registered receivers throws an exception, the + * function will report it using the connection's errorhandlers. It does not + * re-throw the exceptions. + * + * @return Number of notifications processed. + */ + int get_notifs(); + + /// Wait for a notification to come in. + /** There are other events that will also terminate the wait, such as the + * backend failing. It will also wake up periodically. + * + * If a notification comes in, the call will process it, along with any other + * notifications that may have been pending. + * + * To wait for notifications into your own event loop instead, wait until + * there is incoming data on the connection's socket to be read, then call + * @ref get_notifs() repeatedly until it returns zero. + * + * @return Number of notifications processed. + */ + int await_notification(); + + /// Wait for a notification to come in, or for given timeout to pass. + /** There are other events that will also terminate the wait, such as the + * backend failing, or timeout expiring. + * + * If a notification comes in, the call will process it, along with any other + * notifications that may have been pending. + * + * To wait for notifications into your own event loop instead, wait until + * there is incoming data on the connection's socket to be read, then call + * @ref get_notifs repeatedly until it returns zero. + * + * @return Number of notifications processed + */ + int await_notification(std::time_t seconds, long microseconds); + //@} + + /** + * @name Password encryption + * + * Use this when setting a new password for the user if password encryption + * is enabled. Inputs are the SQL name for the user for whom you with to + * encrypt a password; the plaintext password; and the hash algorithm. + * + * The algorithm must be one of "md5", "scram-sha-256" (introduced in + * PostgreSQL 10), or `nullptr`. If the pointer is null, this will query + * the `password_encryption setting` from the server, and use the default + * algorithm as defined there. + * + * @return encrypted version of the password, suitable for encrypted + * PostgreSQL authentication. + * + * Thus you can change a user's password with: + * ```cxx + * void setpw(transaction_base &t, string const &user, string const &pw) + * { + * t.exec0("ALTER USER " + user + " " + * "PASSWORD '" + t.conn().encrypt_password(user,pw) + "'"); + * } + * ``` + * + * When building this against a libpq older than version 10, this will use + * an older function which only supports md5. In that case, requesting a + * different algorithm than md5 will result in a @ref feature_not_supported + * exception. + */ + //@{ + /// Encrypt a password for a given user. + [[nodiscard]] std::string + encrypt_password(zview user, zview password, zview algorithm) + { + return encrypt_password(user.c_str(), password.c_str(), algorithm.c_str()); + } + /// Encrypt a password for a given user. + [[nodiscard]] std::string encrypt_password( + char const user[], char const password[], char const *algorithm = nullptr); + //@} + + /** + * @name Prepared statements + * + * PostgreSQL supports prepared SQL statements, i.e. statements that you can + * register under a name you choose, optimized once by the backend, and + * executed any number of times under the given name. + * + * Prepared statement definitions are not sensitive to transaction + * boundaries. A statement defined inside a transaction will remain defined + * outside that transaction, even if the transaction itself is subsequently + * aborted. Once a statement has been prepared, it will only go away if you + * close the connection or explicitly "unprepare" the statement. + * + * Use the `pqxx::transaction_base::exec_prepared` functions to execute a + * prepared statement. See @ref prepared for a full discussion. + * + * @warning Using prepared statements can save time, but if your statement + * takes parameters, it may also make your application significantly slower! + * The reason is that the server works out a plan for executing the query + * when you prepare it. At that time, of course it does not know the values + * for the parameters that you will pass. If you execute a query without + * preparing it, then the server works out the plan on the spot, with full + * knowledge of the parameter values. + * + * A statement's definition can refer to its parameters as `$1`, `$2`, etc. + * The first parameter you pass to the call provides a value for `$1`, and + * so on. + * + * Here's an example of how to use prepared statements. + * + * ```cxx + * using namespace pqxx; + * void foo(connection &c) + * { + * c.prepare("findtable", "select * from pg_tables where name=$1"); + * work tx{c}; + * result r = tx.exec_prepared("findtable", "mytable"); + * if (std::empty(r)) throw runtime_error{"mytable not found!"}; + * } + * ``` + */ + //@{ + + /// Define a prepared statement. + /** + * @param name unique name for the new prepared statement. + * @param definition SQL statement to prepare. + */ + void prepare(zview name, zview definition) & + { + prepare(name.c_str(), definition.c_str()); + } + + /** + * @param name unique name for the new prepared statement. + * @param definition SQL statement to prepare. + */ + void prepare(char const name[], char const definition[]) &; + + /// Define a nameless prepared statement. + /** + * This can be useful if you merely want to pass large binary parameters to a + * statement without otherwise wishing to prepare it. If you use this + * feature, always keep the definition and the use close together to avoid + * the nameless statement being redefined unexpectedly by code somewhere + * else. + */ + void prepare(char const definition[]) &; + void prepare(zview definition) & { return prepare(definition.c_str()); } + + /// Drop prepared statement. + void unprepare(std::string_view name); + + //@} + + // C++20: constexpr. Breaks ABI. + /// Suffix unique number to name to make it unique within session context. + /** Used internally to generate identifiers for SQL objects (such as cursors + * and nested transactions) based on a given human-readable base name. + */ + [[nodiscard]] std::string adorn_name(std::string_view); + + /** + * @defgroup escaping-functions String-escaping functions + */ + //@{ + + /// Escape string for use as SQL string literal on this connection. + /** @warning This accepts a length, and it does not require a terminating + * zero byte. But if there is a zero byte, escaping stops there even if + * it's not at the end of the string! + */ + [[deprecated("Use std::string_view or pqxx:zview.")]] std::string + esc(char const text[], std::size_t maxlen) const + { + return esc(std::string_view{text, maxlen}); + } + + /// Escape string for use as SQL string literal on this connection. + [[nodiscard]] std::string esc(char const text[]) const + { + return esc(std::string_view{text}); + } + +#if defined(PQXX_HAVE_SPAN) + /// Escape string for use as SQL string literal, into `buffer`. + /** Use this variant when you want to re-use the same buffer across multiple + * calls. If that's not the case, or convenience and simplicity are more + * important, use the single-argument variant. + * + * For every byte in `text`, there must be at least 2 bytes of space in + * `buffer`; plus there must be one byte of space for a trailing zero. + * Throws @ref range_error if this space is not available. + * + * Returns a reference to the escaped string, which is actually stored in + * `buffer`. + */ + [[nodiscard]] std::string_view + esc(std::string_view text, std::span buffer) + { + auto const size{std::size(text)}, space{std::size(buffer)}; + auto const needed{2 * size + 1}; + if (space < needed) + throw range_error{internal::concat( + "Not enough room to escape string of ", size, " byte(s): need ", + needed, " bytes of buffer space, but buffer size is ", space, ".")}; + auto const data{buffer.data()}; + return {data, esc_to_buf(text, data)}; + } +#endif + + /// Escape string for use as SQL string literal on this connection. + /** @warning This is meant for text strings only. It cannot contain bytes + * whose value is zero ("nul bytes"). + */ + [[nodiscard]] std::string esc(std::string_view text) const; + +#if defined(PQXX_HAVE_CONCEPTS) + /// Escape binary string for use as SQL string literal on this connection. + /** This is identical to `esc_raw(data)`. */ + template [[nodiscard]] std::string esc(DATA const &data) const + { + return esc_raw(data); + } +#endif + +#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) + /// Escape binary string for use as SQL string literal, into `buffer`. + /** Use this variant when you want to re-use the same buffer across multiple + * calls. If that's not the case, or convenience and simplicity are more + * important, use the single-argument variant. + * + * For every byte in `data`, there must be at least two bytes of space in + * `buffer`; plus there must be two bytes of space for a header and one for + * a trailing zero. Throws @ref range_error if this space is not available. + * + * Returns a reference to the escaped string, which is actually stored in + * `buffer`. + */ + template + [[nodiscard]] zview esc(DATA const &data, std::span buffer) const + { + auto const size{std::size(data)}, space{std::size(buffer)}; + auto const needed{internal::size_esc_bin(std::size(data))}; + if (space < needed) + throw range_error{internal::concat( + "Not enough room to escape binary string of ", size, " byte(s): need ", + needed, " bytes of buffer space, but buffer size is ", space, ".")}; + + std::basic_string_view view{std::data(data), std::size(data)}; + auto const out{std::data(buffer)}; + // Actually, in the modern format, we know beforehand exactly how many + // bytes we're going to fill. Just leave out the trailing zero. + internal::esc_bin(view, out); + return zview{out, needed - 1}; + } +#endif + + /// Escape binary string for use as SQL string literal on this connection. + [[deprecated("Use std::byte for binary data.")]] std::string + esc_raw(unsigned char const bin[], std::size_t len) const; + + /// Escape binary string for use as SQL string literal on this connection. + /** You can also just use @ref esc with a binary string. */ + [[nodiscard]] std::string esc_raw(std::basic_string_view) const; + +#if defined(PQXX_HAVE_SPAN) + /// Escape binary string for use as SQL string literal, into `buffer`. + /** You can also just use @ref esc with a binary string. */ + [[nodiscard]] std::string + esc_raw(std::basic_string_view, std::span buffer) const; +#endif + +#if defined(PQXX_HAVE_CONCEPTS) + /// Escape binary string for use as SQL string literal on this connection. + /** You can also just use @ref esc with a binary string. */ + template + [[nodiscard]] std::string esc_raw(DATA const &data) const + { + return esc_raw( + std::basic_string_view{std::data(data), std::size(data)}); + } +#endif + +#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) + /// Escape binary string for use as SQL string literal, into `buffer`. + template + [[nodiscard]] zview esc_raw(DATA const &data, std::span buffer) const + { + return this->esc(binary_cast(data), buffer); + } +#endif + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string + unesc_raw(zview text) const + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return unesc_raw(text.c_str()); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string + unesc_raw(char const text[]) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + * + * (The data must be encoded in PostgreSQL's "hex" format. The legacy + * "bytea" escape format, used prior to PostgreSQL 9.0, is no longer + * supported.) + */ + [[nodiscard]] std::basic_string + unesc_bin(std::string_view text) const + { + std::basic_string buf; + buf.resize(pqxx::internal::size_unesc_bin(std::size(text))); + pqxx::internal::unesc_bin(text, buf.data()); + return buf; + } + + /// Escape and quote a string of binary data. + [[deprecated("Use quote(std::basic_string_view).")]] std::string + quote_raw(unsigned char const bin[], std::size_t len) const; + + /// Escape and quote a string of binary data. + std::string quote_raw(std::basic_string_view) const; + +#if defined(PQXX_HAVE_CONCEPTS) + /// Escape and quote a string of binary data. + /** You can also just use @ref quote with binary data. */ + template + [[nodiscard]] std::string quote_raw(DATA const &data) const + { + return quote_raw( + std::basic_string_view{std::data(data), std::size(data)}); + } +#endif + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Escape and quote an SQL identifier for use in a query. + [[nodiscard]] std::string quote_name(std::string_view identifier) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Escape and quote a table name. + /** When passing just a table name, this is just another name for + * @ref quote_name. + */ + [[nodiscard]] std::string quote_table(std::string_view name) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Escape and quote a table path. + /** A table path consists of a table name, optionally prefixed by a schema + * name; and if both are given, they are in turn optionally prefixed by a + * database name. + * + * Each portion of the path (database name, schema name, table name) will be + * quoted separately, and they will be joined together by dots. So for + * example, `myschema.mytable` will become `"myschema"."mytable"`. + */ + [[nodiscard]] std::string quote_table(table_path) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Quote and comma-separate a series of column names. + /** Use this to save a bit of work in cases where you repeatedly need to pass + * the same list of column names, e.g. with @ref stream_to and @ref + * stream_from. Some functions that need to quote the columns list + * internally, will have a "raw" alternative which let you do the quoting + * yourself. It's a bit of extra work, but it can in rare cases let you + * eliminate some duplicate work in quoting them repeatedly. + */ + template + inline std::string quote_columns(STRINGS const &columns) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Represent object as SQL string, including quoting & escaping. + /** + * Recognises nulls and represents them as SQL nulls. They get no quotes. + */ + template + [[nodiscard]] inline std::string quote(T const &t) const; + + [[deprecated("Use std::byte for binary data.")]] std::string + quote(binarystring const &) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Escape and quote binary data for use as a BYTEA value in SQL statement. + [[nodiscard]] std::string + quote(std::basic_string_view bytes) const; + + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Escape string for literal LIKE match. + /** Use this when part of an SQL "LIKE" pattern should match only as a + * literal string, not as a pattern, even if it contains "%" or "_" + * characters that would normally act as wildcards. + * + * The string does not get string-escaped or quoted. You do that later. + * + * For instance, let's say you have a string `name` entered by the user, + * and you're searching a `file` column for items that match `name` + * followed by a dot and three letters. Even if `name` contains wildcard + * characters "%" or "_", you only want those to match literally, so "_" + * only matches "_" and "%" only matches a single "%". + * + * You do that by "like-escaping" `name`, appending the wildcard pattern + * `".___"`, and finally, escaping and quoting the result for inclusion in + * your query: + * + * ```cxx + * tx.exec( + * "SELECT file FROM item WHERE file LIKE " + + * tx.quote(tx.esc_like(name) + ".___")); + * ``` + * + * The SQL "LIKE" operator also lets you choose your own escape character. + * This is supported, but must be a single-byte character. + */ + [[nodiscard]] std::string + esc_like(std::string_view text, char escape_char = '\\') const; + //@} + + /// Attempt to cancel the ongoing query, if any. + /** You can use this from another thread, and/or while a query is executing + * in a pipeline, but it's up to you to ensure that you're not canceling the + * wrong query. This may involve locking. + */ + void cancel_query(); + +#if defined(_WIN32) || __has_include() + /// Set socket to blocking (true) or nonblocking (false). + /** @warning Do not use this unless you _really_ know what you're doing. + * @warning This function is available on most systems, but not necessarily + * all. + */ + void set_blocking(bool block) &; +#endif // defined(_WIN32) || __has_include() + + /// Set session verbosity. + /** Set the verbosity of error messages to "terse", "normal" (the default), + * or "verbose." + * + * If "terse", returned messages include severity, primary text, and + * position only; this will normally fit on a single line. "normal" produces + * messages that include the above plus any detail, hint, or context fields + * (these might span multiple lines). "verbose" includes all available + * fields. + */ + void set_verbosity(error_verbosity verbosity) &noexcept; + + /// Return pointers to the active errorhandlers. + /** The entries are ordered from oldest to newest handler. + * + * You may use this to find errorhandlers that your application wants to + * delete when destroying the connection. Be aware, however, that libpqxx + * may also add errorhandlers of its own, and those will be included in the + * list. If this is a problem for you, derive your errorhandlers from a + * custom base class derived from pqxx::errorhandler. Then use dynamic_cast + * to find which of the error handlers are yours. + * + * The pointers point to the real errorhandlers. The container it returns + * however is a copy of the one internal to the connection, not a reference. + */ + [[nodiscard]] std::vector get_errorhandlers() const; + + /// Return a connection string encapsulating this connection's options. + /** The connection must be currently open for this to work. + * + * Returns a reconstruction of this connection's connection string. It may + * not exactly match the connection string you passed in when creating this + * connection. + */ + [[nodiscard]] std::string connection_string() const; + + /// Explicitly close the connection. + /** The destructor will do this for you automatically. Still, there is a + * reason to `close()` objects explicitly where possible: if an error should + * occur while closing, `close()` can throw an exception. A destructor + * cannot. + * + * Closing a connection is idempotent. Closing a connection that's already + * closed does nothing. + */ + void close(); + + /// Seize control of a raw libpq connection. + /** @warning Do not do this. Please. It's for very rare, very specific + * use-cases. The mechanism may change (or break) in unexpected ways in + * future versions. + * + * @param raw_conn a raw libpq `PQconn` pointer. + */ + static connection seize_raw_connection(internal::pq::PGconn *raw_conn) + { + return connection{raw_conn}; + } + + /// Release the raw connection without closing it. + /** @warning Do not do this. It's for very rare, very specific use-cases. + * The mechanism may change (or break) in unexpected ways in future versions. + * + * The `connection` object becomes unusable after this. + */ + internal::pq::PGconn *release_raw_connection() && + { + return std::exchange(m_conn, nullptr); + } + +private: + friend class connecting; + enum connect_mode + { + connect_nonblocking + }; + connection(connect_mode, zview connection_string); + + /// For use by @ref seize_raw_connection. + explicit connection(internal::pq::PGconn *raw_conn) : m_conn{raw_conn} {} + + /// Poll for ongoing connection, try to progress towards completion. + /** Returns a pair of "now please wait to read data from socket" and "now + * please wait to write data to socket." Both will be false when done. + * + * Throws an exception if polling indicates that the connection has failed. + */ + std::pair poll_connect(); + + // Initialise based on connection string. + void init(char const options[]); + // Initialise based on parameter names and values. + void init(char const *params[], char const *values[]); + void complete_init(); + + result make_result( + internal::pq::PGresult *pgr, std::shared_ptr const &query, + std::string_view desc = ""sv); + + void PQXX_PRIVATE set_up_state(); + + int PQXX_PRIVATE PQXX_PURE status() const noexcept; + + /// Escape a string, into a buffer allocated by the caller. + /** The buffer must have room for at least `2*std::size(text) + 1` bytes. + * + * Returns the number of bytes written, including the trailing zero. + */ + std::size_t esc_to_buf(std::string_view text, char *buf) const; + + friend class internal::gate::const_connection_largeobject; + char const *PQXX_PURE err_msg() const noexcept; + + void PQXX_PRIVATE process_notice_raw(char const msg[]) noexcept; + + result exec_prepared(std::string_view statement, internal::c_params const &); + + /// Throw @ref usage_error if this connection is not in a movable state. + void check_movable() const; + /// Throw @ref usage_error if not in a state where it can be move-assigned. + void check_overwritable() const; + + friend class internal::gate::connection_errorhandler; + void PQXX_PRIVATE register_errorhandler(errorhandler *); + void PQXX_PRIVATE unregister_errorhandler(errorhandler *) noexcept; + + friend class internal::gate::connection_transaction; + result exec(std::string_view, std::string_view = ""sv); + result + PQXX_PRIVATE exec(std::shared_ptr, std::string_view = ""sv); + void PQXX_PRIVATE register_transaction(transaction_base *); + void PQXX_PRIVATE unregister_transaction(transaction_base *) noexcept; + + friend class internal::gate::connection_stream_from; + std::pair>, std::size_t> + PQXX_PRIVATE read_copy_line(); + + friend class internal::gate::connection_stream_to; + void PQXX_PRIVATE write_copy_line(std::string_view); + void PQXX_PRIVATE end_copy_write(); + + friend class internal::gate::connection_largeobject; + internal::pq::PGconn *raw_connection() const { return m_conn; } + + friend class internal::gate::connection_notification_receiver; + void add_receiver(notification_receiver *); + void remove_receiver(notification_receiver *) noexcept; + + friend class internal::gate::connection_pipeline; + void PQXX_PRIVATE start_exec(char const query[]); + bool PQXX_PRIVATE consume_input() noexcept; + bool PQXX_PRIVATE is_busy() const noexcept; + internal::pq::PGresult *get_result(); + + friend class internal::gate::connection_dbtransaction; + friend class internal::gate::connection_sql_cursor; + + result exec_params(std::string_view query, internal::c_params const &args); + + /// Connection handle. + internal::pq::PGconn *m_conn = nullptr; + + /// Active transaction on connection, if any. + /** We don't use this for anything, except to check for open transactions + * when we close the connection or start a new transaction. + * + * We also don't allow move construction or move assignment while there's a + * transaction, since moving the connection in that case would leave one or + * more pointers back from the transaction to the connection dangling. + */ + transaction_base const *m_trans = nullptr; + + std::list m_errorhandlers; + + using receiver_list = + std::multimap; + /// Notification receivers. + receiver_list m_receivers; + + /// Unique number to use as suffix for identifiers (see adorn_name()). + int m_unique_id = 0; +}; + + +/// @deprecated Old base class for connection. They are now the same class. +using connection_base = connection; + + +/// An ongoing, non-blocking stepping stone to a connection. +/** Use this when you want to create a connection to the database, but without + * blocking your whole thread. It is only available on systems that have + * the `` header, and Windows. + * + * Connecting in this way is probably not "faster" (it's more complicated and + * has some extra overhead), but in some situations you can use it to make your + * application as a whole faster. It all depends on having other useful work + * to do in the same thread, and being able to wait on a socket. If you have + * other I/O going on at the same time, your event loop can wait for both the + * libpqxx socket and your own sockets, and wake up whenever any of them is + * ready to do work. + * + * Connecting in this way is not properly "asynchronous;" it's merely + * "nonblocking." This means it's not a super-high-performance mechanism like + * you might get with e.g. `io_uring`. In particular, if we need to look up + * the database hostname in DNS, that will happen synchronously. + * + * To use this, create the `connecting` object, passing a connection string. + * Then loop: If @ref wait_to_read returns true, wait for the socket to have + * incoming data on it. If @ref wait_to_write returns true, wait for the + * socket to be ready for writing. Then call @ref process to process any + * incoming or outgoing data. Do all of this until @ref done returns true (or + * there is an exception). Finally, call @ref produce to get the completed + * connection. + * + * For example: + * + * ```cxx + * pqxx::connecting cg{}; + * + * // Loop until we're done connecting. + * while (!cg.done()) + * { + * wait_for_fd(cg.sock(), cg.wait_to_read(), cg.wait_to_write()); + * cg.process(); + * } + * + * pqxx::connection conn = std::move(cg).produce(); + * + * // At this point, conn is a working connection. You can no longer use + * // cg at all. + * ``` + */ +class PQXX_LIBEXPORT connecting +{ +public: + /// Start connecting. + connecting(zview connection_string = ""_zv); + + connecting(connecting const &) = delete; + connecting(connecting &&) = default; + connecting &operator=(connecting const &) = delete; + connecting &operator=(connecting &&) = default; + + /// Get the socket. The socket may change during the connection process. + [[nodiscard]] int sock() const &noexcept { return m_conn.sock(); } + + /// Should we currently wait to be able to _read_ from the socket? + [[nodiscard]] constexpr bool wait_to_read() const &noexcept + { + return m_reading; + } + + /// Should we currently wait to be able to _write_ to the socket? + [[nodiscard]] constexpr bool wait_to_write() const &noexcept + { + return m_writing; + } + + /// Progress towards completion (but don't block). + void process() &; + + /// Is our connection finished? + [[nodiscard]] constexpr bool done() const &noexcept + { + return not m_reading and not m_writing; + } + + /// Produce the completed connection object. + /** Use this only once, after @ref done returned `true`. Once you have + * called this, the `connecting` instance has no more use or meaning. You + * can't call any of its member functions afterwards. + * + * This member function is rvalue-qualified, meaning that you can only call + * it on an rvalue instance of the class. If what you have is not an rvalue, + * turn it into one by wrapping it in `std::move()`. + */ + [[nodiscard]] connection produce() &&; + +private: + connection m_conn; + bool m_reading{false}; + bool m_writing{true}; +}; + + +template inline std::string connection::quote(T const &t) const +{ + if constexpr (nullness::always_null) + { + return "NULL"; + } + else + { + if (is_null(t)) + return "NULL"; + auto const text{to_string(t)}; + + // Okay, there's an easy way to do this and there's a hard way. The easy + // way was "quote, esc(to_string(t)), quote". I'm going with the hard way + // because it's going to save some string manipulation that will probably + // incur some unnecessary memory allocations and deallocations. + std::string buf{'\''}; + buf.resize(2 + 2 * std::size(text) + 1); + auto const content_bytes{esc_to_buf(text, buf.data() + 1)}; + auto const closing_quote{1 + content_bytes}; + buf[closing_quote] = '\''; + auto const end{closing_quote + 1}; + buf.resize(end); + return buf; + } +} + + +template +inline std::string connection::quote_columns(STRINGS const &columns) const +{ + return separated_list( + ","sv, std::cbegin(columns), std::cend(columns), + [this](auto col) { return this->quote_name(*col); }); +} + + +#if defined(PQXX_HAVE_CONCEPTS) +template +inline connection::connection(MAPPING const ¶ms) +{ + check_version(); + + std::vector keys, values; + if constexpr (std::ranges::sized_range) + { + auto const size{std::ranges::size(params) + 1}; + keys.reserve(size); + values.reserve(size); + } + for (auto const &[key, value] : params) + { + keys.push_back(internal::as_c_string(key)); + values.push_back(internal::as_c_string(value)); + } + keys.push_back(nullptr); + values.push_back(nullptr); + init(std::data(keys), std::data(values)); +} +#endif // PQXX_HAVE_CONCEPTS +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/cursor b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/cursor new file mode 100644 index 000000000..e20b3a4fa --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/cursor @@ -0,0 +1,8 @@ +/** Definition of the iterator/container-style cursor classes. + * + * C++-style wrappers for SQL cursors + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/cursor.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/cursor.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/cursor.hxx new file mode 100644 index 000000000..b392e2407 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/cursor.hxx @@ -0,0 +1,483 @@ +/* Definition of the iterator/container-style cursor classes. + * + * C++-style wrappers for SQL cursors. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/cursor instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_CURSOR +#define PQXX_H_CURSOR + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + +#include "pqxx/result.hxx" +#include "pqxx/transaction_base.hxx" + + +namespace pqxx +{ +/// Common definitions for cursor types +/** In C++ terms, fetches are always done in pre-increment or pre-decrement + * fashion--i.e. the result does not include the row the cursor is on at the + * beginning of the fetch, and the cursor ends up being positioned on the last + * row in the result. + * + * There are singular positions akin to `end()` at both the beginning and the + * end of the cursor's range of movement, although these fit in so naturally + * with the semantics that one rarely notices them. The cursor begins at the + * first of these, but any fetch in the forward direction will move the cursor + * off this position and onto the first row before returning anything. + */ +class PQXX_LIBEXPORT cursor_base +{ +public: + using size_type = result_size_type; + using difference_type = result_difference_type; + + /// Cursor access-pattern policy + /** Allowing a cursor to move forward only can result in better performance, + * so use this access policy whenever possible. + */ + enum access_policy + { + /// Cursor can move forward only + forward_only, + /// Cursor can move back and forth + random_access + }; + + /// Cursor update policy + /** + * @warning Not all PostgreSQL versions support updatable cursors. + */ + enum update_policy + { + /// Cursor can be used to read data but not to write + read_only, + /// Cursor can be used to update data as well as read it + update + }; + + /// Cursor destruction policy + /** The normal thing to do is to make a cursor object the owner of the SQL + * cursor it represents. There may be cases, however, where a cursor needs + * to persist beyond the end of the current transaction (and thus also beyond + * the lifetime of the cursor object that created it!), where it can be + * "adopted" into a new cursor object. See the basic_cursor documentation + * for an explanation of cursor adoption. + * + * If a cursor is created with "loose" ownership policy, the object + * representing the underlying SQL cursor will not take the latter with it + * when its own lifetime ends, nor will its originating transaction. + * + * @warning Use this feature with care and moderation. Only one cursor + * object should be responsible for any one underlying SQL cursor at any + * given time. + */ + enum ownership_policy + { + /// Destroy SQL cursor when cursor object is closed at end of transaction + owned, + /// Leave SQL cursor in existence after close of object and transaction + loose + }; + + cursor_base() = delete; + cursor_base(cursor_base const &) = delete; + cursor_base &operator=(cursor_base const &) = delete; + + /** + * @name Special movement distances. + */ + //@{ + + // TODO: Make constexpr inline (but breaks ABI). + /// Special value: read until end. + /** @return Maximum value for result::difference_type, so the cursor will + * attempt to read the largest possible result set. + */ + [[nodiscard]] static difference_type all() noexcept; + + /// Special value: read one row only. + /** @return Unsurprisingly, 1. + */ + [[nodiscard]] static constexpr difference_type next() noexcept { return 1; } + + /// Special value: read backwards, one row only. + /** @return Unsurprisingly, -1. + */ + [[nodiscard]] static constexpr difference_type prior() noexcept + { + return -1; + } + + // TODO: Make constexpr inline (but breaks ABI). + /// Special value: read backwards from current position back to origin. + /** @return Minimum value for result::difference_type. + */ + [[nodiscard]] static difference_type backward_all() noexcept; + + //@} + + /// Name of underlying SQL cursor + /** + * @returns Name of SQL cursor, which may differ from original given name. + * @warning Don't use this to access the SQL cursor directly without going + * through the provided wrapper classes! + */ + [[nodiscard]] constexpr std::string const &name() const noexcept + { + return m_name; + } + +protected: + cursor_base(connection &, std::string_view Name, bool embellish_name = true); + + std::string const m_name; +}; +} // namespace pqxx + + +#include + + +namespace pqxx +{ +/// "Stateless cursor" class: easy API for retrieving parts of result sets +/** This is a front-end for SQL cursors, but with a more C++-like API. + * + * Actually, stateless_cursor feels entirely different from SQL cursors. You + * don't keep track of positions, fetches, and moves; you just say which rows + * you want. See the retrieve() member function. + */ +template +class stateless_cursor +{ +public: + using size_type = result_size_type; + using difference_type = result_difference_type; + + /// Create cursor. + /** + * @param tx The transaction within which you want to create the cursor. + * @param query The SQL query whose results the cursor should traverse. + * @param cname A hint for the cursor's name. The actual SQL cursor's name + * will be based on this (though not necessarily identical). + * @param hold Create a `WITH HOLD` cursor? Such cursors stay alive after + * the transaction has ended, so you can continue to use it. + */ + stateless_cursor( + transaction_base &tx, std::string_view query, std::string_view cname, + bool hold) : + m_cur{tx, query, cname, cursor_base::random_access, up, op, hold} + {} + + /// Adopt an existing scrolling SQL cursor. + /** This lets you define a cursor yourself, and then wrap it in a + * libpqxx-managed `stateless_cursor` object. + * + * @param tx The transaction within which you want to manage the cursor. + * @param adopted_cursor Your cursor's SQL name. + */ + stateless_cursor(transaction_base &tx, std::string_view adopted_cursor) : + m_cur{tx, adopted_cursor, op} + { + // Put cursor in known position + m_cur.move(cursor_base::backward_all()); + } + + /// Close this cursor. + /** The destructor will do this for you automatically. + * + * Closing a cursor is idempotent. Closing a cursor that's already closed + * does nothing. + */ + void close() noexcept { m_cur.close(); } + + /// Number of rows in cursor's result set + /** @note This function is not const; it may need to scroll to find the size + * of the result set. + */ + [[nodiscard]] size_type size() + { + return internal::obtain_stateless_cursor_size(m_cur); + } + + /// Retrieve rows from begin_pos (inclusive) to end_pos (exclusive) + /** Rows are numbered starting from 0 to size()-1. + * + * @param begin_pos First row to retrieve. May be one row beyond the end of + * the result set, to avoid errors for empty result sets. Otherwise, must be + * a valid row number in the result set. + * @param end_pos Row up to which to fetch. Rows are returned ordered from + * begin_pos to end_pos, i.e. in ascending order if begin_pos < end_pos but + * in descending order if begin_pos > end_pos. The end_pos may be + * arbitrarily inside or outside the result set; only existing rows are + * included in the result. + */ + result retrieve(difference_type begin_pos, difference_type end_pos) + { + return internal::stateless_cursor_retrieve( + m_cur, result::difference_type(size()), begin_pos, end_pos); + } + + /// Return this cursor's name. + [[nodiscard]] constexpr std::string const &name() const noexcept + { + return m_cur.name(); + } + +private: + internal::sql_cursor m_cur; +}; + + +class icursor_iterator; +} // namespace pqxx + + +namespace pqxx::internal::gate +{ +class icursor_iterator_icursorstream; +class icursorstream_icursor_iterator; +} // namespace pqxx::internal::gate + + +namespace pqxx +{ +/// Simple read-only cursor represented as a stream of results +/** SQL cursors can be tricky, especially in C++ since the two languages seem + * to have been designed on different planets. An SQL cursor has two singular + * positions akin to `end()` on either side of the underlying result set. + * + * These cultural differences are hidden from view somewhat by libpqxx, which + * tries to make SQL cursors behave more like familiar C++ entities such as + * iterators, sequences, streams, and containers. + * + * Data is fetched from the cursor as a sequence of result objects. Each of + * these will contain the number of rows defined as the stream's stride, except + * of course the last block of data which may contain fewer rows. + * + * This class can create or adopt cursors that live outside any backend + * transaction, which your backend version may not support. + */ +class PQXX_LIBEXPORT icursorstream +{ +public: + using size_type = cursor_base::size_type; + using difference_type = cursor_base::difference_type; + + /// Set up a read-only, forward-only cursor. + /** Roughly equivalent to a C++ Standard Library istream, this cursor type + * supports only two operations: reading a block of rows while moving + * forward, and moving forward without reading any data. + * + * @param context Transaction context in which this cursor will be active. + * @param query SQL query whose results this cursor shall iterate. + * @param basename Suggested name for the SQL cursor; the library will append + * a unique code to ensure its uniqueness. + * @param sstride Number of rows to fetch per read operation; must be a + * positive number. + */ + icursorstream( + transaction_base &context, std::string_view query, + std::string_view basename, difference_type sstride = 1); + + /// Adopt existing SQL cursor. Use with care. + /** Forms a cursor stream around an existing SQL cursor, as returned by e.g. + * a server-side function. The SQL cursor will be cleaned up by the stream's + * destructor as if it had been created by the stream; cleaning it up by hand + * or adopting the same cursor twice is an error. + * + * Passing the name of the cursor as a string is not allowed, both to avoid + * confusion with the other constructor and to discourage unnecessary use of + * adopted cursors. + * + * @warning It is technically possible to adopt a "WITH HOLD" cursor, i.e. a + * cursor that stays alive outside its creating transaction. However, any + * cursor stream (including the underlying SQL cursor, naturally) must be + * destroyed before its transaction context object is destroyed. Therefore + * the only way to use SQL's WITH HOLD feature is to adopt the cursor, but + * defer doing so until after entering the transaction context that will + * eventually destroy it. + * + * @param context Transaction context in which this cursor will be active. + * @param cname Result field containing the name of the SQL cursor to adopt. + * @param sstride Number of rows to fetch per read operation; must be a + * positive number. + * @param op Ownership policy. Determines whether the cursor underlying this + * stream will be destroyed when the stream is closed. + */ + icursorstream( + transaction_base &context, field const &cname, difference_type sstride = 1, + cursor_base::ownership_policy op = cursor_base::owned); + + /// Return `true` if this stream may still return more data. + constexpr operator bool() const &noexcept { return not m_done; } + + /// Read new value into given result object; same as operator `>>`. + /** The result set may continue any number of rows from zero to the chosen + * stride, inclusive. An empty result will only be returned if there are no + * more rows to retrieve. + * + * @param res Write the retrieved data into this result object. + * @return Reference to this very stream, to facilitate "chained" invocations + * ("C.get(r1).get(r2);") + */ + icursorstream &get(result &res) + { + res = fetchblock(); + return *this; + } + /// Read new value into given result object; same as `get(result&)`. + /** The result set may continue any number of rows from zero to the chosen + * stride, inclusive. An empty result will only be returned if there are no + * more rows to retrieve. + * + * @param res Write the retrieved data into this result object. + * @return Reference to this very stream, to facilitate "chained" invocations + * ("C >> r1 >> r2;") + */ + icursorstream &operator>>(result &res) { return get(res); } + + /// Move given number of rows forward without reading data. + /** Ignores any stride that you may have set. It moves by a given number of + * rows, not a number of strides. + * + * @return Reference to this stream itself, to facilitate "chained" + * invocations. + */ + icursorstream &ignore(std::streamsize n = 1) &; + + /// Change stride, i.e. the number of rows to fetch per read operation. + /** + * @param stride Must be a positive number. + */ + void set_stride(difference_type stride) &; + [[nodiscard]] constexpr difference_type stride() const noexcept + { + return m_stride; + } + +private: + result fetchblock(); + + friend class internal::gate::icursorstream_icursor_iterator; + size_type forward(size_type n = 1); + void insert_iterator(icursor_iterator *) noexcept; + void remove_iterator(icursor_iterator *) const noexcept; + + void service_iterators(difference_type); + + internal::sql_cursor m_cur; + + difference_type m_stride; + difference_type m_realpos, m_reqpos; + + mutable icursor_iterator *m_iterators; + + bool m_done; +}; + + +/// Approximate istream_iterator for icursorstream. +/** Intended as an implementation of an input_iterator (as defined by the C++ + * Standard Library), this class supports only two basic operations: reading + * the current element, and moving forward. In addition to the minimal + * guarantees for istream_iterators, this class supports multiple successive + * reads of the same position (the current result set is cached in the + * iterator) even after copying and even after new data have been read from the + * stream. This appears to be a requirement for input_iterators. Comparisons + * are also supported in the general case. + * + * The iterator does not care about its own position, however. Moving an + * iterator forward moves the underlying stream forward and reads the data from + * the new stream position, regardless of the iterator's old position in the + * stream. + * + * The stream's stride defines the granularity for all iterator movement or + * access operations, i.e. "ici += 1" advances the stream by one stride's worth + * of rows, and "*ici++" reads one stride's worth of rows from the stream. + * + * @warning Do not read from the underlying stream or its cursor, move its read + * position, or change its stride, between the time the first icursor_iterator + * on it is created and the time its last icursor_iterator is destroyed. + * + * @warning Manipulating these iterators within the context of a single cursor + * stream is not thread-safe. Creating a new iterator, copying one, + * or destroying one affects the stream as a whole. + */ +class PQXX_LIBEXPORT icursor_iterator +{ +public: + using iterator_category = std::input_iterator_tag; + using value_type = result; + using pointer = result const *; + using reference = result const &; + using istream_type = icursorstream; + using size_type = istream_type::size_type; + using difference_type = istream_type::difference_type; + + icursor_iterator() noexcept; + explicit icursor_iterator(istream_type &) noexcept; + icursor_iterator(icursor_iterator const &) noexcept; + ~icursor_iterator() noexcept; + + result const &operator*() const + { + refresh(); + return m_here; + } + result const *operator->() const + { + refresh(); + return &m_here; + } + icursor_iterator &operator++(); + icursor_iterator operator++(int); + icursor_iterator &operator+=(difference_type); + icursor_iterator &operator=(icursor_iterator const &) noexcept; + + [[nodiscard]] bool operator==(icursor_iterator const &rhs) const; + [[nodiscard]] bool operator!=(icursor_iterator const &rhs) const noexcept + { + return not operator==(rhs); + } + [[nodiscard]] bool operator<(icursor_iterator const &rhs) const; + [[nodiscard]] bool operator>(icursor_iterator const &rhs) const + { + return rhs < *this; + } + [[nodiscard]] bool operator<=(icursor_iterator const &rhs) const + { + return not(*this > rhs); + } + [[nodiscard]] bool operator>=(icursor_iterator const &rhs) const + { + return not(*this < rhs); + } + +private: + void refresh() const; + + friend class internal::gate::icursor_iterator_icursorstream; + difference_type pos() const noexcept { return m_pos; } + void fill(result const &); + + icursorstream *m_stream{nullptr}; + result m_here; + difference_type m_pos; + icursor_iterator *m_prev{nullptr}, *m_next{nullptr}; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/dbtransaction b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/dbtransaction new file mode 100644 index 000000000..fa8d26476 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/dbtransaction @@ -0,0 +1,8 @@ +/** pqxx::dbtransaction abstract base class. + * + * pqxx::dbransaction defines a real transaction on the database. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/dbtransaction.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/dbtransaction.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/dbtransaction.hxx new file mode 100644 index 000000000..d85cb170f --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/dbtransaction.hxx @@ -0,0 +1,70 @@ +/* Definition of the pqxx::dbtransaction abstract base class. + * + * pqxx::dbransaction defines a real transaction on the database. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/dbtransaction instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_DBTRANSACTION +#define PQXX_H_DBTRANSACTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/transaction_base.hxx" + +namespace pqxx +{ +/// Abstract transaction base class: bracket transactions on the database. +/** + * @ingroup transactions + * + * Use a dbtransaction-derived object such as "work" (transaction<>) to enclose + * operations on a database in a single "unit of work." This ensures that the + * whole series of operations either succeeds as a whole or fails completely. + * In no case will it leave half-finished work behind in the database. + * + * Once processing on a transaction has succeeded and any changes should be + * allowed to become permanent in the database, call commit(). If something + * has gone wrong and the changes should be forgotten, call abort() instead. + * If you do neither, an implicit abort() is executed at destruction time. + * + * It is an error to abort a transaction that has already been committed, or to + * commit a transaction that has already been aborted. Aborting an already + * aborted transaction or committing an already committed one is allowed, to + * make error handling easier. Repeated aborts or commits have no effect after + * the first one. + * + * Database transactions are not suitable for guarding long-running processes. + * If your transaction code becomes too long or too complex, consider ways to + * break it up into smaller ones. Unfortunately there is no universal recipe + * for this. + * + * The actual operations for committing/aborting the backend transaction are + * implemented by a derived class. The implementing concrete class must also + * call @ref close from its destructor. + */ +class PQXX_LIBEXPORT PQXX_NOVTABLE dbtransaction : public transaction_base +{ +protected: + /// Begin transaction. + explicit dbtransaction(connection &c) : transaction_base{c} {} + /// Begin transaction. + dbtransaction(connection &c, std::string_view tname) : + transaction_base{c, tname} + {} + /// Begin transaction. + dbtransaction( + connection &c, std::string_view tname, + std::shared_ptr rollback_cmd) : + transaction_base{c, tname, rollback_cmd} + {} +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/errorhandler b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/errorhandler new file mode 100644 index 000000000..ea572ee79 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/errorhandler @@ -0,0 +1,8 @@ +/** pqxx::errorhandler class. + * + * Callbacks for handling errors and warnings. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/errorhandler.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/errorhandler.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/errorhandler.hxx new file mode 100644 index 000000000..2ffb5703c --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/errorhandler.hxx @@ -0,0 +1,92 @@ +/* Definition of the pqxx::errorhandler class. + * + * pqxx::errorhandler handlers errors and warnings in a database session. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/errorhandler instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ERRORHANDLER +#define PQXX_H_ERRORHANDLER + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/types.hxx" + + +namespace pqxx::internal::gate +{ +class errorhandler_connection; +} + + +namespace pqxx +{ +/** + * @addtogroup errorhandler + */ +//@{ + +/// Base class for error-handler callbacks. +/** To receive errors and warnings from a connection, subclass this with your + * own error-handler functor, and instantiate it for the connection. Destroying + * the handler un-registers it. + * + * A connection can have multiple error handlers at the same time. When the + * database connection emits an error or warning message, it passes the message + * to each error handler, starting with the most recently registered one and + * progressing towards the oldest one. However an error handler may also + * instruct the connection not to pass the message to further handlers by + * returning "false." + * + * @warning Strange things happen when a result object outlives its parent + * connection. If you register an error handler on a connection, then you must + * not access the result after destroying the connection. This applies even if + * you destroy the error handler first! + */ +class PQXX_LIBEXPORT errorhandler +{ +public: + explicit errorhandler(connection &); + virtual ~errorhandler(); + + /// Define in subclass: receive an error or warning message from the + /// database. + /** + * @return Whether the same error message should also be passed to the + * remaining, older errorhandlers. + */ + virtual bool operator()(char const msg[]) noexcept = 0; + + errorhandler() = delete; + errorhandler(errorhandler const &) = delete; + errorhandler &operator=(errorhandler const &) = delete; + +private: + connection *m_home; + + friend class internal::gate::errorhandler_connection; + void unregister() noexcept; +}; + + +/// An error handler that suppresses any previously registered error handlers. +class quiet_errorhandler : public errorhandler +{ +public: + /// Suppress error notices. + quiet_errorhandler(connection &conn) : errorhandler{conn} {} + + /// Revert to previous handling of error notices. + virtual bool operator()(char const[]) noexcept override { return false; } +}; + +//@} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/except b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/except new file mode 100644 index 000000000..e5dd508bf --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/except @@ -0,0 +1,8 @@ +/** libpqxx exception classes. + * + * pqxx::sql_error, pqxx::broken_connection, pqxx::in_doubt_error, ... + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/except.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/except.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/except.hxx new file mode 100644 index 000000000..24f959437 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/except.hxx @@ -0,0 +1,447 @@ +/* Definition of libpqxx exception classes. + * + * pqxx::sql_error, pqxx::broken_connection, pqxx::in_doubt_error, ... + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/except instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_EXCEPT +#define PQXX_H_EXCEPT + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + + +namespace pqxx +{ +/** + * @addtogroup exception Exception classes + * + * These exception classes follow, roughly, the two-level hierarchy defined by + * the PostgreSQL SQLSTATE error codes (see Appendix A of the PostgreSQL + * documentation corresponding to your server version). This is not a complete + * mapping though. There are other differences as well, e.g. the error code + * for `statement_completion_unknown` has a separate status in libpqxx as + * @ref in_doubt_error, and `too_many_connections` is classified as a + * `broken_connection` rather than a subtype of `insufficient_resources`. + * + * @see http://www.postgresql.org/docs/9.4/interactive/errcodes-appendix.html + * + * @{ + */ + +/// Run-time failure encountered by libpqxx, similar to std::runtime_error. +struct PQXX_LIBEXPORT failure : std::runtime_error +{ + explicit failure(std::string const &); +}; + + +/// Exception class for lost or failed backend connection. +/** + * @warning When this happens on Unix-like systems, you may also get a SIGPIPE + * signal. That signal aborts the program by default, so if you wish to be + * able to continue after a connection breaks, be sure to disarm this signal. + * + * If you're working on a Unix-like system, see the manual page for + * `signal` (2) on how to deal with SIGPIPE. The easiest way to make this + * signal harmless is to make your program ignore it: + * + * ```cxx + * #include + * + * int main() + * { + * signal(SIGPIPE, SIG_IGN); + * // ... + * ``` + */ +struct PQXX_LIBEXPORT broken_connection : failure +{ + broken_connection(); + explicit broken_connection(std::string const &); +}; + + +/// The caller attempted to set a variable to null, which is not allowed. +struct PQXX_LIBEXPORT variable_set_to_null : failure +{ + variable_set_to_null(); + explicit variable_set_to_null(std::string const &); +}; + + +/// Exception class for failed queries. +/** Carries, in addition to a regular error message, a copy of the failed query + * and (if available) the SQLSTATE value accompanying the error. + */ +class PQXX_LIBEXPORT sql_error : public failure +{ + /// Query string. Empty if unknown. + std::string const m_query; + /// SQLSTATE string describing the error type, if known; or empty string. + std::string const m_sqlstate; + +public: + explicit sql_error( + std::string const &whatarg = "", std::string const &Q = "", + char const sqlstate[] = nullptr); + virtual ~sql_error() noexcept override; + + /// The query whose execution triggered the exception + [[nodiscard]] PQXX_PURE std::string const &query() const noexcept; + + /// SQLSTATE error code if known, or empty string otherwise. + [[nodiscard]] PQXX_PURE std::string const &sqlstate() const noexcept; +}; + + +/// "Help, I don't know whether transaction was committed successfully!" +/** Exception that might be thrown in rare cases where the connection to the + * database is lost while finishing a database transaction, and there's no way + * of telling whether it was actually executed by the backend. In this case + * the database is left in an indeterminate (but consistent) state, and only + * manual inspection will tell which is the case. + */ +struct PQXX_LIBEXPORT in_doubt_error : failure +{ + explicit in_doubt_error(std::string const &); +}; + + +/// The backend saw itself forced to roll back the ongoing transaction. +struct PQXX_LIBEXPORT transaction_rollback : sql_error +{ + explicit transaction_rollback( + std::string const &whatarg, std::string const &q = "", + char const sqlstate[] = nullptr); +}; + + +/// Transaction failed to serialize. Please retry it. +/** Can only happen at transaction isolation levels REPEATABLE READ and + * SERIALIZABLE. + * + * The current transaction cannot be committed without violating the guarantees + * made by its isolation level. This is the effect of a conflict with another + * ongoing transaction. The transaction may still succeed if you try to + * perform it again. + */ +struct PQXX_LIBEXPORT serialization_failure : transaction_rollback +{ + explicit serialization_failure( + std::string const &whatarg, std::string const &q, + char const sqlstate[] = nullptr); +}; + + +/// We can't tell whether our last statement succeeded. +struct PQXX_LIBEXPORT statement_completion_unknown : transaction_rollback +{ + explicit statement_completion_unknown( + std::string const &whatarg, std::string const &q, + char const sqlstate[] = nullptr); +}; + + +/// The ongoing transaction has deadlocked. Retrying it may help. +struct PQXX_LIBEXPORT deadlock_detected : transaction_rollback +{ + explicit deadlock_detected( + std::string const &whatarg, std::string const &q, + char const sqlstate[] = nullptr); +}; + + +/// Internal error in libpqxx library +struct PQXX_LIBEXPORT internal_error : std::logic_error +{ + explicit internal_error(std::string const &); +}; + + +/// Error in usage of libpqxx library, similar to std::logic_error +struct PQXX_LIBEXPORT usage_error : std::logic_error +{ + explicit usage_error(std::string const &); +}; + + +/// Invalid argument passed to libpqxx, similar to std::invalid_argument +struct PQXX_LIBEXPORT argument_error : std::invalid_argument +{ + explicit argument_error(std::string const &); +}; + + +/// Value conversion failed, e.g. when converting "Hello" to int. +struct PQXX_LIBEXPORT conversion_error : std::domain_error +{ + explicit conversion_error(std::string const &); +}; + + +/// Could not convert value to string: not enough buffer space. +struct PQXX_LIBEXPORT conversion_overrun : conversion_error +{ + explicit conversion_overrun(std::string const &); +}; + + +/// Something is out of range, similar to std::out_of_range +struct PQXX_LIBEXPORT range_error : std::out_of_range +{ + explicit range_error(std::string const &); +}; + + +/// Query returned an unexpected number of rows. +struct PQXX_LIBEXPORT unexpected_rows : public range_error +{ + explicit unexpected_rows(std::string const &msg) : range_error{msg} {} +}; + + +/// Database feature not supported in current setup. +struct PQXX_LIBEXPORT feature_not_supported : sql_error +{ + explicit feature_not_supported( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +/// Error in data provided to SQL statement. +struct PQXX_LIBEXPORT data_exception : sql_error +{ + explicit data_exception( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT integrity_constraint_violation : sql_error +{ + explicit integrity_constraint_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT restrict_violation : integrity_constraint_violation +{ + explicit restrict_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + integrity_constraint_violation{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT not_null_violation : integrity_constraint_violation +{ + explicit not_null_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + integrity_constraint_violation{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT foreign_key_violation : integrity_constraint_violation +{ + explicit foreign_key_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + integrity_constraint_violation{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT unique_violation : integrity_constraint_violation +{ + explicit unique_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + integrity_constraint_violation{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT check_violation : integrity_constraint_violation +{ + explicit check_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + integrity_constraint_violation{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT invalid_cursor_state : sql_error +{ + explicit invalid_cursor_state( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT invalid_sql_statement_name : sql_error +{ + explicit invalid_sql_statement_name( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT invalid_cursor_name : sql_error +{ + explicit invalid_cursor_name( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT syntax_error : sql_error +{ + /// Approximate position in string where error occurred, or -1 if unknown. + int const error_position; + + explicit syntax_error( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, int pos = -1) : + sql_error{err, Q, sqlstate}, error_position{pos} + {} +}; + +struct PQXX_LIBEXPORT undefined_column : syntax_error +{ + explicit undefined_column( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + syntax_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT undefined_function : syntax_error +{ + explicit undefined_function( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + syntax_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT undefined_table : syntax_error +{ + explicit undefined_table( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + syntax_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT insufficient_privilege : sql_error +{ + explicit insufficient_privilege( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +/// Resource shortage on the server +struct PQXX_LIBEXPORT insufficient_resources : sql_error +{ + explicit insufficient_resources( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT disk_full : insufficient_resources +{ + explicit disk_full( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + insufficient_resources{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT out_of_memory : insufficient_resources +{ + explicit out_of_memory( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + insufficient_resources{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT too_many_connections : broken_connection +{ + explicit too_many_connections(std::string const &err) : + broken_connection{err} + {} +}; + +/// PL/pgSQL error +/** Exceptions derived from this class are errors from PL/pgSQL procedures. + */ +struct PQXX_LIBEXPORT plpgsql_error : sql_error +{ + explicit plpgsql_error( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + sql_error{err, Q, sqlstate} + {} +}; + +/// Exception raised in PL/pgSQL procedure +struct PQXX_LIBEXPORT plpgsql_raise : plpgsql_error +{ + explicit plpgsql_raise( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + plpgsql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT plpgsql_no_data_found : plpgsql_error +{ + explicit plpgsql_no_data_found( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + plpgsql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT plpgsql_too_many_rows : plpgsql_error +{ + explicit plpgsql_too_many_rows( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr) : + plpgsql_error{err, Q, sqlstate} + {} +}; + +struct PQXX_LIBEXPORT blob_already_exists : failure +{ + explicit blob_already_exists(std::string const &); +}; + +/** + * @} + */ +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/field b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/field new file mode 100644 index 000000000..37cb69e84 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/field @@ -0,0 +1,8 @@ +/** pqxx::field class. + * + * pqxx::field refers to a field in a query result. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/field.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/field.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/field.hxx new file mode 100644 index 000000000..b8b869fe4 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/field.hxx @@ -0,0 +1,542 @@ +/* Definitions for the pqxx::field class. + * + * pqxx::field refers to a field in a query result. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/field instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_FIELD +#define PQXX_H_FIELD + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#include "pqxx/array.hxx" +#include "pqxx/composite.hxx" +#include "pqxx/result.hxx" +#include "pqxx/strconv.hxx" +#include "pqxx/types.hxx" + +namespace pqxx +{ +/// Reference to a field in a result set. +/** A field represents one entry in a row. It represents an actual value + * in the result set, and can be converted to various types. + */ +class PQXX_LIBEXPORT field +{ +public: + using size_type = field_size_type; + + /// Constructor. Do not call this yourself; libpqxx will do it for you. + /** Create field as reference to a field in a result set. + * @param r Row that this field is part of. + * @param c Column number of this field. + */ + [[deprecated( + "Do not construct fields yourself. Get them from the row.")]] field(row const &r, row_size_type c) noexcept; + + /// Constructor. Do not call this yourself; libpqxx will do it for you. + [[deprecated( + "Do not construct fields yourself. Get them from the " + "row.")]] field() noexcept = default; + + /** + * @name Comparison + */ + //@{ + // TODO: noexcept. Breaks ABI. + /// Byte-by-byte comparison of two fields (all nulls are considered equal) + /** @warning null handling is still open to discussion and change! + * + * Handling of null values differs from that in SQL where a comparison + * involving a null value yields null, so nulls are never considered equal + * to one another or even to themselves. + * + * Null handling also probably differs from the closest equivalent in C++, + * which is the NaN (Not-a-Number) value, a singularity comparable to + * SQL's null. This is because the builtin == operator demands that a == a. + * + * The usefulness of this operator is questionable. No interpretation + * whatsoever is imposed on the data; 0 and 0.0 are considered different, + * as are null vs. the empty string, or even different (but possibly + * equivalent and equally valid) encodings of the same Unicode character + * etc. + */ + [[nodiscard]] PQXX_PURE bool operator==(field const &) const; + + /// Byte-by-byte comparison (all nulls are considered equal) + /** @warning See operator==() for important information about this operator + */ + [[nodiscard]] PQXX_PURE bool operator!=(field const &rhs) const noexcept + { + return not operator==(rhs); + } + //@} + + /** + * @name Column information + */ + //@{ + /// Column name. + [[nodiscard]] PQXX_PURE char const *name() const &; + + /// Column type. + [[nodiscard]] oid PQXX_PURE type() const; + + /// What table did this column come from? + [[nodiscard]] PQXX_PURE oid table() const; + + /// Return row number. The first row is row 0, the second is row 1, etc. + PQXX_PURE constexpr row_size_type num() const noexcept { return col(); } + + /// What column number in its originating table did this column come from? + [[nodiscard]] PQXX_PURE row_size_type table_column() const; + //@} + + /** + * @name Content access + */ + //@{ + /// Read as `string_view`, or an empty one if null. + /** The result only remains usable while the data for the underlying + * @ref result exists. Once all `result` objects referring to that data have + * been destroyed, the `string_view` will no longer point to valid memory. + */ + [[nodiscard]] PQXX_PURE std::string_view view() const & + { + return std::string_view(c_str(), size()); + } + + /// Read as plain C string. + /** Since the field's data is stored internally in the form of a + * zero-terminated C string, this is the fastest way to read it. Use the + * to() or as() functions to convert the string to other types such as + * `int`, or to C++ strings. + * + * Do not use this for BYTEA values, or other binary values. To read those, + * convert the value to your desired type using `to()` or `as()`. For + * example: `f.as>()`. + */ + [[nodiscard]] PQXX_PURE char const *c_str() const &; + + /// Is this field's value null? + [[nodiscard]] PQXX_PURE bool is_null() const noexcept; + + /// Return number of bytes taken up by the field's value. + [[nodiscard]] PQXX_PURE size_type size() const noexcept; + + /// Read value into obj; or if null, leave obj untouched and return `false`. + /** This can be used with optional types (except pointers other than C-style + * strings). + */ + template + auto to(T &obj) const -> typename std::enable_if_t< + (not std::is_pointer::value or std::is_same::value), + bool> + { + if (is_null()) + { + return false; + } + else + { + auto const bytes{c_str()}; + from_string(bytes, obj); + return true; + } + } + + /// Read field as a composite value, write its components into `fields`. + /** @warning This is still experimental. It may change or be replaced. + * + * Returns whether the field was null. If it was, it will not touch the + * values in `fields`. + */ + template bool composite_to(T &...fields) const + { + if (is_null()) + { + return false; + } + else + { + parse_composite(m_home.m_encoding, view(), fields...); + return true; + } + } + + /// Read value into obj; or leave obj untouched and return `false` if null. + template bool operator>>(T &obj) const { return to(obj); } + + /// Read value into obj; or if null, use default value and return `false`. + /** This can be used with `std::optional`, as well as with standard smart + * pointer types, but not with raw pointers. If the conversion from a + * PostgreSQL string representation allocates a pointer (e.g. using `new`), + * then the object's later deallocation should be baked in as well, right + * from the point where the object is created. So if you want a pointer, use + * a smart pointer, not a raw pointer. + * + * There is one exception, of course: C-style strings. Those are just + * pointers to the field's internal text data. + */ + template + auto to(T &obj, T const &default_value) const -> typename std::enable_if_t< + (not std::is_pointer::value or std::is_same::value), + bool> + { + bool const null{is_null()}; + if (null) + obj = default_value; + else + obj = from_string(this->view()); + return not null; + } + + /// Return value as object of given type, or default value if null. + /** Note that unless the function is instantiated with an explicit template + * argument, the Default value's type also determines the result type. + */ + template T as(T const &default_value) const + { + if (is_null()) + return default_value; + else + return from_string(this->view()); + } + + /// Return value as object of given type, or throw exception if null. + /** Use as `as>()` or `as()` as + * an alternative to `get()`; this is disabled for use with raw pointers + * (other than C-strings) because storage for the value can't safely be + * allocated here + */ + template T as() const + { + if (is_null()) + { + if constexpr (not nullness::has_null) + internal::throw_null_conversion(type_name); + else + return nullness::null(); + } + else + { + return from_string(this->view()); + } + } + + /// Return value wrapped in some optional type (empty for nulls). + /** Use as `get()` as before to obtain previous behavior, or specify + * container type with `get()` + */ + template class O = std::optional> + constexpr O get() const + { + return as>(); + } + + // TODO: constexpr noexcept, once array_parser constructor gets those. + /// Parse the field as an SQL array. + /** Call the parser to retrieve values (and structure) from the array. + * + * Make sure the @ref result object stays alive until parsing is finished. If + * you keep the @ref row of `field` object alive, it will keep the @ref + * result object alive as well. + */ + array_parser as_array() const & + { + return array_parser{c_str(), m_home.m_encoding}; + } + //@} + + +protected: + constexpr result const &home() const noexcept { return m_home; } + constexpr result::size_type idx() const noexcept { return m_row; } + constexpr row_size_type col() const noexcept { return m_col; } + + // TODO: Create gates. + friend class pqxx::result; + friend class pqxx::row; + field( + result const &r, result_size_type row_num, row_size_type col_num) noexcept + : + m_col{col_num}, m_home{r}, m_row{row_num} + {} + + /** + * You'd expect this to be unsigned, but due to the way reverse iterators + * are related to regular iterators, it must be allowed to underflow to -1. + */ + row_size_type m_col; + +private: + result m_home; + result::size_type m_row; +}; + + +template<> inline bool field::to(std::string &obj) const +{ + bool const null{is_null()}; + if (not null) + obj = std::string{view()}; + return not null; +} + + +template<> +inline bool field::to( + std::string &obj, std::string const &default_value) const +{ + bool const null{is_null()}; + if (null) + obj = default_value; + else + obj = std::string{view()}; + return not null; +} + + +/// Specialization: `to(char const *&)`. +/** The buffer has the same lifetime as the data in this result (i.e. of this + * result object, or the last remaining one copied from it etc.), so take care + * not to use it after the last result object referring to this query result is + * destroyed. + */ +template<> inline bool field::to(char const *&obj) const +{ + bool const null{is_null()}; + if (not null) + obj = c_str(); + return not null; +} + + +template<> inline bool field::to(std::string_view &obj) const +{ + bool const null{is_null()}; + if (not null) + obj = view(); + return not null; +} + + +template<> +inline bool field::to( + std::string_view &obj, std::string_view const &default_value) const +{ + bool const null{is_null()}; + if (null) + obj = default_value; + else + obj = view(); + return not null; +} + + +template<> inline std::string_view field::as() const +{ + if (is_null()) + PQXX_UNLIKELY + internal::throw_null_conversion(type_name); + return view(); +} + + +template<> +inline std::string_view +field::as(std::string_view const &default_value) const +{ + return is_null() ? default_value : view(); +} + + +template<> inline bool field::to(zview &obj) const +{ + bool const null{is_null()}; + if (not null) + obj = zview{c_str(), size()}; + return not null; +} + + +template<> +inline bool field::to(zview &obj, zview const &default_value) const +{ + bool const null{is_null()}; + if (null) + obj = default_value; + else + obj = zview{c_str(), size()}; + return not null; +} + + +template<> inline zview field::as() const +{ + if (is_null()) + PQXX_UNLIKELY + internal::throw_null_conversion(type_name); + return zview{c_str(), size()}; +} + + +template<> inline zview field::as(zview const &default_value) const +{ + return is_null() ? default_value : zview{c_str(), size()}; +} + + +template> +class field_streambuf : public std::basic_streambuf +{ +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + using openmode = std::ios::openmode; + using seekdir = std::ios::seekdir; + + explicit field_streambuf(field const &f) : m_field{f} { initialize(); } + +protected: + virtual int sync() override { return traits_type::eof(); } + + virtual pos_type seekoff(off_type, seekdir, openmode) override + { + return traits_type::eof(); + } + virtual pos_type seekpos(pos_type, openmode) override + { + return traits_type::eof(); + } + virtual int_type overflow(int_type) override { return traits_type::eof(); } + virtual int_type underflow() override { return traits_type::eof(); } + +private: + field const &m_field; + + int_type initialize() + { + auto g{static_cast(const_cast(m_field.c_str()))}; + this->setg(g, g, g + std::size(m_field)); + return int_type(std::size(m_field)); + } +}; + + +/// Input stream that gets its data from a result field +/** Use this class exactly as you would any other istream to read data from a + * field. All formatting and streaming operations of `std::istream` are + * supported. What you'll typically want to use, however, is the fieldstream + * alias (which defines a @ref basic_fieldstream for `char`). This is similar + * to how e.g. `std::ifstream` relates to `std::basic_ifstream`. + * + * This class has only been tested for the char type (and its default traits). + */ +template> +class basic_fieldstream : public std::basic_istream +{ + using super = std::basic_istream; + +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + + basic_fieldstream(field const &f) : super{nullptr}, m_buf{f} + { + super::init(&m_buf); + } + +private: + field_streambuf m_buf; +}; + +using fieldstream = basic_fieldstream; + +/// Write a result field to any type of stream +/** This can be convenient when writing a field to an output stream. More + * importantly, it lets you write a field to e.g. a `stringstream` which you + * can then use to read, format and convert the field in ways that to() does + * not support. + * + * Example: parse a field into a variable of the nonstandard + * "long long" type. + * + * ```cxx + * extern result R; + * long long L; + * stringstream S; + * + * // Write field's string into S + * S << R[0][0]; + * + * // Parse contents of S into L + * S >> L; + * ``` + */ +template +inline std::basic_ostream & +operator<<(std::basic_ostream &s, field const &value) +{ + s.write(value.c_str(), std::streamsize(std::size(value))); + return s; +} + + +/// Convert a field's value to type `T`. +/** Unlike the "regular" `from_string`, this knows how to deal with null + * values. + */ +template inline T from_string(field const &value) +{ + if (value.is_null()) + { + if constexpr (nullness::has_null) + return nullness::null(); + else + internal::throw_null_conversion(type_name); + } + else + { + return from_string(value.view()); + } +} + + +/// Convert a field's value to `nullptr_t`. +/** Yes, you read that right. This conversion does nothing useful. It always + * returns `nullptr`. + * + * Except... what if the field is not null? In that case, this throws + * @ref conversion_error. + */ +template<> +inline std::nullptr_t from_string(field const &value) +{ + if (not value.is_null()) + throw conversion_error{ + "Extracting non-null field into nullptr_t variable."}; + return nullptr; +} + + +/// Convert a field to a string. +template<> PQXX_LIBEXPORT std::string to_string(field const &value); +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/array-composite.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/array-composite.hxx new file mode 100644 index 000000000..d2b6603e5 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/array-composite.hxx @@ -0,0 +1,305 @@ +#if !defined(PQXX_ARRAY_COMPOSITE_HXX) +# define PQXX_ARRAY_COMPOSITE_HXX + +# include + +# include "pqxx/strconv.hxx" + +namespace pqxx::internal +{ +// Find the end of a double-quoted string. +/** `input[pos]` must be the opening double quote. + * + * Returns the offset of the first position after the closing quote. + */ +inline std::size_t scan_double_quoted_string( + char const input[], std::size_t size, std::size_t pos, + pqxx::internal::glyph_scanner_func *scan) +{ + // XXX: find_char<'"', '\\'>(). + auto next{scan(input, size, pos)}; + bool at_quote{false}; + for (pos = next, next = scan(input, size, pos); pos < size; + pos = next, next = scan(input, size, pos)) + { + if (at_quote) + { + if (next - pos == 1 and input[pos] == '"') + { + // We just read a pair of double quotes. Carry on. + at_quote = false; + } + else + { + // We just read one double quote, and now we're at a character that's + // not a second double quote. Ergo, that last character was the + // closing double quote and this is the position right after it. + return pos; + } + } + else if (next - pos == 1) + { + switch (input[pos]) + { + case '\\': + // Backslash escape. Skip ahead by one more character. + pos = next; + next = scan(input, size, pos); + break; + + case '"': + // This is either the closing double quote, or the first of a pair of + // double quotes. + at_quote = true; + break; + } + } + else + { + // Multibyte character. Carry on. + } + } + if (not at_quote) + throw argument_error{ + "Missing closing double-quote: " + std::string{input}}; + return pos; +} + + +/// Un-quote and un-escape a double-quoted SQL string. +inline std::string parse_double_quoted_string( + char const input[], std::size_t end, std::size_t pos, + pqxx::internal::glyph_scanner_func *scan) +{ + std::string output; + // Maximum output size is same as the input size, minus the opening and + // closing quotes. Or in the extreme opposite case, the real number could be + // half that. Usually it'll be a pretty close estimate. + output.reserve(std::size_t(end - pos - 2)); + + for (auto here{scan(input, end, pos)}, next{scan(input, end, here)}; + here < end - 1; here = next, next = scan(input, end, here)) + { + // A backslash here is always an escape. So is a double-quote, since we're + // inside the double-quoted string. In either case, we can just ignore the + // escape character and use the next character. This is the one redeeming + // feature of SQL's escaping system. + if ((next - here == 1) and (input[here] == '\\' or input[here] == '"')) + { + // Skip escape. + here = next; + next = scan(input, end, here); + } + output.append(input + here, input + next); + } + return output; +} + + +/// Find the end of an unquoted string in an array or composite-type value. +/** Stops when it gets to the end of the input; or when it sees any of the + * characters in STOP which has not been escaped. + * + * For array values, STOP is a comma, a semicolon, or a closing brace. For + * a value of a composite type, STOP is a comma or a closing parenthesis. + */ +template +inline std::size_t scan_unquoted_string( + char const input[], std::size_t size, std::size_t pos, + pqxx::internal::glyph_scanner_func *scan) +{ + bool at_backslash{false}; + auto next{scan(input, size, pos)}; + while ((pos < size) and + ((next - pos) > 1 or at_backslash or ((input[pos] != STOP) and ...))) + { + pos = next; + next = scan(input, size, pos); + at_backslash = + ((not at_backslash) and ((next - pos) == 1) and (input[pos] == '\\')); + } + return pos; +} + + +/// Parse an unquoted array entry or cfield of a composite-type field. +inline std::string parse_unquoted_string( + char const input[], std::size_t end, std::size_t pos, + pqxx::internal::glyph_scanner_func *scan) +{ + std::string output; + bool at_backslash{false}; + output.reserve(end - pos); + for (auto next{scan(input, end, pos)}; pos < end; + pos = next, next = scan(input, end, pos)) + { + at_backslash = + ((not at_backslash) and ((next - pos) == 1) and (input[pos] == '\\')); + if (not at_backslash) + output.append(input + pos, next - pos); + } + return output; +} + + +/// Parse a field of a composite-type value. +/** `T` is the C++ type of the field we're parsing, and `index` is its + * zero-based number. + * + * Strip off the leading parenthesis or bracket yourself before parsing. + * However, this function will parse the lcosing parenthesis or bracket. + * + * After a successful parse, `pos` will point at `std::end(text)`. + * + * For the purposes of parsing, ranges and arrays count as compositve values, + * so this function supports parsing those. If you specifically need a closing + * parenthesis, check afterwards that `text` did not end in a bracket instead. + * + * @param index Index of the current field, zero-based. It will increment for + * the next field. + * @param input Full input text for the entire composite-type value. + * @param pos Starting position (in `input`) of the field that we're parsing. + * After parsing, this will point at the beginning of the next field if + * there is one, or one position past the last character otherwise. + * @param field Destination for the parsed value. + * @param scan Glyph scanning function for the relevant encoding type. + * @param last_field Number of the last field in the value (zero-based). When + * parsing the last field, this will equal `index`. + */ +template +inline void parse_composite_field( + std::size_t &index, std::string_view input, std::size_t &pos, T &field, + glyph_scanner_func *scan, std::size_t last_field) +{ + assert(index <= last_field); + auto next{scan(std::data(input), std::size(input), pos)}; + if ((next - pos) != 1) + throw conversion_error{"Non-ASCII character in composite-type syntax."}; + + // Expect a field. + switch (input[pos]) + { + case ',': + case ')': + case ']': + // The field is empty, i.e, null. + if constexpr (nullness::has_null) + field = nullness::null(); + else + throw conversion_error{ + "Can't read composite field " + to_string(index) + ": C++ type " + + type_name + " does not support nulls."}; + break; + + case '"': { + auto const stop{scan_double_quoted_string( + std::data(input), std::size(input), pos, scan)}; + auto const text{ + parse_double_quoted_string(std::data(input), stop, pos, scan)}; + field = from_string(text); + pos = stop; + } + break; + + default: { + auto const stop{scan_unquoted_string<',', ')', ']'>( + std::data(input), std::size(input), pos, scan)}; + auto const text{parse_unquoted_string(std::data(input), stop, pos, scan)}; + field = from_string(text); + pos = stop; + } + break; + } + + // Expect a comma or a closing parenthesis. + next = scan(std::data(input), std::size(input), pos); + + if ((next - pos) != 1) + throw conversion_error{ + "Unexpected non-ASCII character after composite field: " + + std::string{input}}; + + if (index < last_field) + { + if (input[pos] != ',') + throw conversion_error{ + "Found '" + std::string{input[pos]} + + "' in composite value where comma was expected: " + std::data(input)}; + } + else + { + if (input[pos] == ',') + throw conversion_error{ + "Composite value contained more fields than the expected " + + to_string(last_field) + ": " + std::data(input)}; + if (input[pos] != ')' and input[pos] != ']') + throw conversion_error{ + "Composite value has unexpected characters where closing parenthesis " + "was expected: " + + std::string{input}}; + if (next != std::size(input)) + throw conversion_error{ + "Composite value has unexpected text after closing parenthesis: " + + std::string{input}}; + } + + pos = next; + ++index; +} + + +/// Conservatively estimate buffer size needed for a composite field. +template +inline std::size_t size_composite_field_buffer(T const &field) +{ + if constexpr (is_unquoted_safe) + { + // Safe to copy, without quotes or escaping. Drop the terminating zero. + return size_buffer(field) - 1; + } + else + { + // + Opening quote. + // + Field budget. + // - Terminating zero. + // + Escaping for each byte in the field's string representation. + // - Escaping for terminating zero. + // + Closing quote. + return 1 + 2 * (size_buffer(field) - 1) + 1; + } +} + + +template +inline void write_composite_field(char *&pos, char *end, T const &field) +{ + if constexpr (is_unquoted_safe) + { + // No need for quoting or escaping. Convert it straight into its final + // place in the buffer, and "backspace" the trailing zero. + pos = string_traits::into_buf(pos, end, field) - 1; + } + else + { + // The field may need escaping, which means we need an intermediate buffer. + // To avoid allocating that at run time, we use the end of the buffer that + // we have. + auto const budget{size_buffer(field)}; + *pos++ = '"'; + + // Now escape buf into its final position. + for (char const c : string_traits::to_buf(end - budget, end, field)) + { + if ((c == '"') or (c == '\\')) + *pos++ = '\\'; + + *pos++ = c; + } + + *pos++ = '"'; + } + + *pos++ = ','; +} +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/callgate.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/callgate.hxx new file mode 100644 index 000000000..42f7703e3 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/callgate.hxx @@ -0,0 +1,70 @@ +#ifndef PQXX_H_CALLGATE +#define PQXX_H_CALLGATE + +/* +Here's what a typical gate class definition looks like: + +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE @gateclass@ : callgate<@host@> +{ + friend class @client@; + + @gateclass@(reference x) : super(x) {} + + // Methods here. Use home() to access the host-class object. +}; +} // namespace pqxx::internal::gate +*/ + +namespace pqxx::internal +{ +/// Base class for call gates. +/** + * A call gate defines a limited, private interface on the host class that + * specified client classes can access. + * + * The metaphor works as follows: the gate stands in front of a "home," which + * is really a class, and only lets specific friends in. + * + * To implement a call gate that gives client C access to host H, + * * derive a gate class from callgate; + * * make the gate class a friend of H; + * * make C a friend of the gate class; and + * * implement "stuff C can do with H" as private members in the gate class. + * + * This special kind of "gated" friendship gives C private access to H, but + * only through an expressly limited interface. The gate class can access its + * host object as home(). + * + * Keep gate classes entirely stateless. They should be ultra-lightweight + * wrappers for their host classes, and be optimized away as much as possible + * by the compiler. Once you start adding state, you're on a slippery slope + * away from the pure, clean, limited interface pattern that gate classes are + * meant to implement. + * + * Ideally, all member functions of the gate class should be one-liners passing + * calls straight on to the host class. It can be useful however to break this + * rule temporarily during inter-class refactoring. + */ +template class PQXX_PRIVATE callgate +{ +protected: + /// This class, to keep constructors easy. + using super = callgate; + /// A reference to the host class. Helps keep constructors easy. + using reference = HOME &; + + callgate(reference x) : m_home(x) {} + + /// The home object. The gate class has full "private" access. + reference home() const noexcept { return m_home; } + +private: + reference m_home; +}; +} // namespace pqxx::internal + +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/concat.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/concat.hxx new file mode 100644 index 000000000..cd28bde7c --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/concat.hxx @@ -0,0 +1,45 @@ +#if !defined(PQXX_CONCAT_HXX) +# define PQXX_CONCAT_HXX + +# include +# include + +# include "pqxx/strconv.hxx" + +namespace pqxx::internal +{ +/// Convert item to a string, write it into [here, end). +template +void render_item(TYPE const &item, char *&here, char *end) +{ + here = string_traits::into_buf(here, end, item) - 1; +} + + +// C++20: Support non-random_access_range ranges. +/// Efficiently combine a bunch of items into one big string. +/** Use this as an optimised version of string concatentation. It takes just + * about any type; it will represent each item as a string according to its + * @ref string_traits. + * + * This is a simpler, more specialised version of @ref separated_list for a + * statically known series of items, possibly of different types. + */ +template +[[nodiscard]] inline std::string concat(TYPE... item) +{ + std::string buf; + // Size to accommodate string representations of all inputs, minus their + // terminating zero bytes. + buf.resize(size_buffer(item...)); + + char *const data{buf.data()}; + char *here = data; + char *end = data + std::size(buf); + (render_item(item, here, end), ...); + + buf.resize(static_cast(here - data)); + return buf; +} +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/conversions.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/conversions.hxx new file mode 100644 index 000000000..1df4fdead --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/conversions.hxx @@ -0,0 +1,1188 @@ +#include +#include +#include +#include +#include +#include + +#if defined(PQXX_HAVE_SPAN) && __has_include() +# include +#endif + +#include +#include +#include + +#include "pqxx/types.hxx" +#include "pqxx/util.hxx" + + +/* Internal helpers for string conversion, and conversion implementations. + * + * Do not include this header directly. The libpqxx headers do it for you. + */ +namespace pqxx::internal +{ +/// Convert a number in [0, 9] to its ASCII digit. +inline constexpr char number_to_digit(int i) noexcept +{ + return static_cast(i + '0'); +} + + +/// Compute numeric value of given textual digit (assuming that it is a digit). +constexpr int digit_to_number(char c) noexcept +{ + return c - '0'; +} + + +/// Summarize buffer overrun. +/** Don't worry about the exact parameter types: the sizes will be reasonably + * small, and nonnegative. + */ +std::string PQXX_LIBEXPORT +state_buffer_overrun(int have_bytes, int need_bytes); + + +template +inline std::string state_buffer_overrun(HAVE have_bytes, NEED need_bytes) +{ + return state_buffer_overrun( + static_cast(have_bytes), static_cast(need_bytes)); +} + + +/// Throw exception for attempt to convert null to given type. +[[noreturn]] PQXX_LIBEXPORT void +throw_null_conversion(std::string const &type); + + +/// Deliberately nonfunctional conversion traits for `char` types. +/** There are no string conversions for `char` and its signed and unsigned + * variants. Such a conversion would be dangerously ambiguous: should we treat + * it as text, or as a small integer? It'd be an open invitation for bugs. + * + * But the error message when you get this wrong is very cryptic. So, we + * derive dummy @ref string_traits implementations from this dummy type, and + * ensure that the compiler disallows their use. The compiler error message + * will at least contain a hint of the root of the problem. + */ +template struct disallowed_ambiguous_char_conversion +{ + static char *into_buf(char *, char *, CHAR_TYPE) = delete; + static constexpr zview + to_buf(char *, char *, CHAR_TYPE const &) noexcept = delete; + + static constexpr std::size_t + size_buffer(CHAR_TYPE const &) noexcept = delete; + static CHAR_TYPE from_string(std::string_view) = delete; +}; + + +template PQXX_LIBEXPORT extern std::string to_string_float(T); + + +/// Generic implementation for into_buf, on top of to_buf. +template +inline char *generic_into_buf(char *begin, char *end, T const &value) +{ + zview const text{string_traits::to_buf(begin, end, value)}; + auto const space{end - begin}; + // Include the trailing zero. + auto const len = std::size(text) + 1; + if (internal::cmp_greater(len, space)) + throw conversion_overrun{ + "Not enough buffer space to insert " + type_name + ". " + + state_buffer_overrun(space, len)}; + std::memmove(begin, text.data(), len); + return begin + len; +} + + +/// String traits for builtin integral types (though not bool). +template struct integral_traits +{ + static PQXX_LIBEXPORT T from_string(std::string_view text); + static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value); + static PQXX_LIBEXPORT char *into_buf(char *begin, char *end, T const &value); + + static constexpr std::size_t size_buffer(T const &) noexcept + { + /** Includes a sign if needed; the number of base-10 digits which the type + * can reliably represent; the one extra base-10 digit which the type can + * only partially represent; and the terminating zero. + */ + return std::is_signed_v + std::numeric_limits::digits10 + 1 + 1; + } +}; + + +/// String traits for builtin floating-point types. +template struct float_traits +{ + static PQXX_LIBEXPORT T from_string(std::string_view text); + static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value); + static PQXX_LIBEXPORT char *into_buf(char *begin, char *end, T const &value); + + // Return a nonnegative integral value's number of decimal digits. + static constexpr std::size_t digits10(std::size_t value) noexcept + { + if (value < 10) + return 1; + else + return 1 + digits10(value / 10); + } + + static constexpr std::size_t size_buffer(T const &) noexcept + { + using lims = std::numeric_limits; + // See #328 for a detailed discussion on the maximum number of digits. + // + // In a nutshell: for the big cases, the scientific notation is always + // the shortest one, and therefore the one that to_chars will pick. + // + // So... How long can the scientific notation get? 1 (for sign) + 1 (for + // decimal point) + 1 (for 'e') + 1 (for exponent sign) + max_digits10 + + // max number of digits in the exponent + 1 (terminating zero). + // + // What's the max number of digits in the exponent? It's the max number of + // digits out of the most negative exponent and the most positive one. + // + // The longest positive exponent is easy: 1 + ceil(log10(max_exponent10)). + // (The extra 1 is because 10^n takes up 1 + n digits, not n.) + // + // The longest negative exponent is a bit harder: min_exponent10 gives us + // the smallest power of 10 which a normalised version of T can represent. + // But the smallest denormalised power of 10 that T can represent is + // another max_digits10 powers of 10 below that. + // needs a minus sign. + // + // All this stuff messes with my head a bit because it's on the order of + // log10(log10(n)). It's easy to get the number of logs wrong. + auto const max_pos_exp{digits10(lims::max_exponent10)}; + // Really want std::abs(lims::min_exponent10), but MSVC 2017 apparently has + // problems with std::abs. So we use -lims::min_exponent10 instead. + auto const max_neg_exp{ + digits10(lims::max_digits10 - lims::min_exponent10)}; + return 1 + // Sign. + 1 + // Decimal point. + std::numeric_limits::max_digits10 + // Mantissa digits. + 1 + // Exponent "e". + 1 + // Exponent sign. + // Spell this weirdly to stop Windows compilers from reading this as + // a call to their "max" macro when NOMINMAX is not defined. + (std::max)(max_pos_exp, max_neg_exp) + // Exponent digits. + 1; // Terminating zero. + } +}; +} // namespace pqxx::internal + + +namespace pqxx +{ +/// The built-in arithmetic types do not have inherent null values. +template +struct nullness>> : no_null +{}; + + +template<> struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> +struct string_traits + : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> +struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> +struct string_traits : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> +struct string_traits + : internal::integral_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> struct string_traits : internal::float_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> struct string_traits : internal::float_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; +template<> +struct string_traits : internal::float_traits +{}; +template<> inline constexpr bool is_unquoted_safe{true}; + + +template<> struct string_traits +{ + static PQXX_LIBEXPORT bool from_string(std::string_view text); + + static constexpr zview to_buf(char *, char *, bool const &value) noexcept + { + return value ? "true"_zv : "false"_zv; + } + + static char *into_buf(char *begin, char *end, bool const &value) + { + return pqxx::internal::generic_into_buf(begin, end, value); + } + + static constexpr std::size_t size_buffer(bool const &) noexcept { return 6; } +}; + + +/// We don't support conversion to/from `char` types. +/** Why are these disallowed? Because they are ambiguous. It's not inherently + * clear whether we should treat values of these types as text or as small + * integers. Either choice would lead to bugs. + */ +template<> +struct string_traits + : internal::disallowed_ambiguous_char_conversion +{}; + +/// We don't support conversion to/from `char` types. +/** Why are these disallowed? Because they are ambiguous. It's not inherently + * clear whether we should treat values of these types as text or as small + * integers. Either choice would lead to bugs. + */ +template<> +struct string_traits + : internal::disallowed_ambiguous_char_conversion +{}; + +/// We don't support conversion to/from `char` types. +/** Why are these disallowed? Because they are ambiguous. It's not inherently + * clear whether we should treat values of these types as text or as small + * integers. Either choice would lead to bugs. + */ +template<> +struct string_traits + : internal::disallowed_ambiguous_char_conversion +{}; + + +template<> inline constexpr bool is_unquoted_safe{true}; + + +template struct nullness> +{ + static constexpr bool has_null = true; + /// Technically, you could have an optional of an always-null type. + static constexpr bool always_null = nullness::always_null; + static constexpr bool is_null(std::optional const &v) noexcept + { + return ((not v.has_value()) or pqxx::is_null(*v)); + } + static constexpr std::optional null() { return {}; } +}; + + +template +inline constexpr format param_format(std::optional const &value) +{ + return param_format(*value); +} + + +template struct string_traits> +{ + static char *into_buf(char *begin, char *end, std::optional const &value) + { + return string_traits::into_buf(begin, end, *value); + } + + static zview to_buf(char *begin, char *end, std::optional const &value) + { + if (value.has_value()) + return string_traits::to_buf(begin, end, *value); + else + return {}; + } + + static std::optional from_string(std::string_view text) + { + return std::optional{ + std::in_place, string_traits::from_string(text)}; + } + + static std::size_t size_buffer(std::optional const &value) noexcept + { + return pqxx::size_buffer(value.value()); + } +}; + + +template +inline constexpr bool is_unquoted_safe>{is_unquoted_safe}; + + +template struct nullness> +{ + static constexpr bool has_null = (nullness::has_null or ...); + static constexpr bool always_null = (nullness::always_null and ...); + static constexpr bool is_null(std::variant const &value) noexcept + { + return std::visit( + [](auto const &i) noexcept { + return nullness>::is_null(i); + }, + value); + } + + // We don't support `null()` for `std::variant`. + /** It would be technically possible to have a `null` in the case where just + * one of the types has a null, but it gets complicated and arbitrary. + */ + static constexpr std::variant null() = delete; +}; + + +template struct string_traits> +{ + static char * + into_buf(char *begin, char *end, std::variant const &value) + { + return std::visit( + [begin, end](auto const &i) { + return string_traits>::into_buf(begin, end, i); + }, + value); + } + static zview to_buf(char *begin, char *end, std::variant const &value) + { + return std::visit( + [begin, end](auto const &i) { + return string_traits>::to_buf(begin, end, i); + }, + value); + } + static std::size_t size_buffer(std::variant const &value) noexcept + { + return std::visit( + [](auto const &i) noexcept { return pqxx::size_buffer(i); }, value); + } + + /** There's no from_string for std::variant. We could have one with a rule + * like "pick the first type which fits the value," but we'd have to look + * into how natural that API feels to users. + */ + static std::variant from_string(std::string_view) = delete; +}; + + +template +inline constexpr format param_format(std::variant const &value) +{ + return std::visit([](auto &v) { return param_format(v); }, value); +} + + +template +inline constexpr bool is_unquoted_safe>{ + (is_unquoted_safe and ...)}; + + +template inline T from_string(std::stringstream const &text) +{ + return from_string(text.str()); +} + + +template<> struct string_traits +{ + static char *into_buf(char *, char *, std::nullptr_t) = delete; + + static constexpr zview + to_buf(char *, char *, std::nullptr_t const &) noexcept + { + return {}; + } + + static constexpr std::size_t size_buffer(std::nullptr_t = nullptr) noexcept + { + return 0; + } + static std::nullptr_t from_string(std::string_view) = delete; +}; + + +template<> struct string_traits +{ + static char *into_buf(char *, char *, std::nullopt_t) = delete; + + static constexpr zview + to_buf(char *, char *, std::nullopt_t const &) noexcept + { + return {}; + } + + static constexpr std::size_t size_buffer(std::nullopt_t) noexcept + { + return 0; + } + static std::nullopt_t from_string(std::string_view) = delete; +}; + + +template<> struct string_traits +{ + static char *into_buf(char *, char *, std::monostate) = delete; + + static constexpr zview + to_buf(char *, char *, std::monostate const &) noexcept + { + return {}; + } + + static constexpr std::size_t size_buffer(std::monostate) noexcept + { + return 0; + } + static std::monostate from_string(std::string_view) = delete; +}; + + +template<> inline constexpr bool is_unquoted_safe{true}; + + +template<> struct nullness +{ + static constexpr bool has_null = true; + static constexpr bool always_null = false; + static constexpr bool is_null(char const *t) noexcept + { + return t == nullptr; + } + static constexpr char const *null() noexcept { return nullptr; } +}; + + +/// String traits for C-style string ("pointer to char const"). +template<> struct string_traits +{ + static char const *from_string(std::string_view text) { return text.data(); } + + static zview to_buf(char *begin, char *end, char const *const &value) + { + return generic_to_buf(begin, end, value); + } + + static char *into_buf(char *begin, char *end, char const *const &value) + { + auto const space{end - begin}; + // Count the trailing zero, even though std::strlen() and friends don't. + auto const len{std::strlen(value) + 1}; + if (space < ptrdiff_t(len)) + throw conversion_overrun{ + "Could not copy string: buffer too small. " + + pqxx::internal::state_buffer_overrun(space, len)}; + std::memmove(begin, value, len); + return begin + len; + } + + static std::size_t size_buffer(char const *const &value) noexcept + { + return std::strlen(value) + 1; + } +}; + + +template<> struct nullness +{ + static constexpr bool has_null = true; + static constexpr bool always_null = false; + static constexpr bool is_null(char const *t) noexcept + { + return t == nullptr; + } + static constexpr char const *null() { return nullptr; } +}; + + +/// String traits for non-const C-style string ("pointer to char"). +template<> struct string_traits +{ + static char *into_buf(char *begin, char *end, char *const &value) + { + return string_traits::into_buf(begin, end, value); + } + static zview to_buf(char *begin, char *end, char *const &value) + { + return string_traits::to_buf(begin, end, value); + } + static std::size_t size_buffer(char *const &value) noexcept + { + return string_traits::size_buffer(value); + } + + /// Don't allow conversion to this type since it breaks const-safety. + static char *from_string(std::string_view) = delete; +}; + + +template struct nullness : no_null +{}; + + +/// String traits for C-style string constant ("array of char"). +/** @warning This assumes that every array-of-char is a C-style string literal. + * So, it must include a trailing zero. and it must have static duration. + */ +template struct string_traits +{ + static constexpr zview + to_buf(char *, char *, char const (&value)[N]) noexcept + { + return zview{value, N - 1}; + } + + static char *into_buf(char *begin, char *end, char const (&value)[N]) + { + if (internal::cmp_less(end - begin, size_buffer(value))) + throw conversion_overrun{ + "Could not convert char[] to string: too long for buffer."}; + std::memcpy(begin, value, N); + return begin + N; + } + static constexpr std::size_t size_buffer(char const (&)[N]) noexcept + { + return N; + } + + /// Don't allow conversion to this type. + static void from_string(std::string_view) = delete; +}; + + +template<> struct nullness : no_null +{}; + + +template<> struct string_traits +{ + static std::string from_string(std::string_view text) + { + return std::string{text}; + } + + static char *into_buf(char *begin, char *end, std::string const &value) + { + if (internal::cmp_greater_equal(std::size(value), end - begin)) + throw conversion_overrun{ + "Could not convert string to string: too long for buffer."}; + // Include the trailing zero. + value.copy(begin, std::size(value)); + begin[std::size(value)] = '\0'; + return begin + std::size(value) + 1; + } + + static zview to_buf(char *begin, char *end, std::string const &value) + { + return generic_to_buf(begin, end, value); + } + + static std::size_t size_buffer(std::string const &value) noexcept + { + return std::size(value) + 1; + } +}; + + +/// There's no real null for `std::string_view`. +/** I'm not sure how clear-cut this is: a `string_view` may have a null + * data pointer, which is analogous to a null `char` pointer. + */ +template<> struct nullness : no_null +{}; + + +/// String traits for `string_view`. +template<> struct string_traits +{ + static constexpr std::size_t + size_buffer(std::string_view const &value) noexcept + { + return std::size(value) + 1; + } + + static char *into_buf(char *begin, char *end, std::string_view const &value) + { + if (internal::cmp_greater_equal(std::size(value), end - begin)) + throw conversion_overrun{ + "Could not store string_view: too long for buffer."}; + value.copy(begin, std::size(value)); + begin[std::size(value)] = '\0'; + return begin + std::size(value) + 1; + } + + /// Don't convert to this type; it has nowhere to store its contents. + static std::string_view from_string(std::string_view) = delete; +}; + + +template<> struct nullness : no_null +{}; + + +/// String traits for `zview`. +template<> struct string_traits +{ + static constexpr std::size_t + size_buffer(std::string_view const &value) noexcept + { + return std::size(value) + 1; + } + + static char *into_buf(char *begin, char *end, zview const &value) + { + auto const size{std::size(value)}; + if (internal::cmp_less_equal(end - begin, std::size(value))) + throw conversion_overrun{"Not enough buffer space to store this zview."}; + value.copy(begin, size); + begin[size] = '\0'; + return begin + size + 1; + } + + static std::string_view to_buf(char *begin, char *end, zview const &value) + { + return {into_buf(begin, end, value), std::size(value)}; + } + + /// Don't convert to this type; it has nowhere to store its contents. + static zview from_string(std::string_view) = delete; +}; + + +template<> struct nullness : no_null +{}; + + +template<> struct string_traits +{ + static std::size_t size_buffer(std::stringstream const &) = delete; + + static std::stringstream from_string(std::string_view text) + { + std::stringstream stream; + stream.write(text.data(), std::streamsize(std::size(text))); + return stream; + } + + static char *into_buf(char *, char *, std::stringstream const &) = delete; + static std::string_view + to_buf(char *, char *, std::stringstream const &) = delete; +}; + + +template<> struct nullness +{ + static constexpr bool has_null = true; + static constexpr bool always_null = true; + static constexpr bool is_null(std::nullptr_t const &) noexcept + { + return true; + } + static constexpr std::nullptr_t null() noexcept { return nullptr; } +}; + + +template<> struct nullness +{ + static constexpr bool has_null = true; + static constexpr bool always_null = true; + static constexpr bool is_null(std::nullopt_t const &) noexcept + { + return true; + } + static constexpr std::nullopt_t null() noexcept { return std::nullopt; } +}; + + +template<> struct nullness +{ + static constexpr bool has_null = true; + static constexpr bool always_null = true; + static constexpr bool is_null(std::monostate const &) noexcept + { + return true; + } + static constexpr std::monostate null() noexcept { return {}; } +}; + + +template struct nullness> +{ + static constexpr bool has_null = true; + static constexpr bool always_null = false; + static constexpr bool is_null(std::unique_ptr const &t) noexcept + { + return not t or pqxx::is_null(*t); + } + static constexpr std::unique_ptr null() { return {}; } +}; + + +template +struct string_traits> +{ + static std::unique_ptr from_string(std::string_view text) + { + return std::make_unique(string_traits::from_string(text)); + } + + static char * + into_buf(char *begin, char *end, std::unique_ptr const &value) + { + return string_traits::into_buf(begin, end, *value); + } + + static zview + to_buf(char *begin, char *end, std::unique_ptr const &value) + { + if (value) + return string_traits::to_buf(begin, end, *value); + else + return {}; + } + + static std::size_t + size_buffer(std::unique_ptr const &value) noexcept + { + return pqxx::size_buffer(*value.get()); + } +}; + + +template +inline format param_format(std::unique_ptr const &value) +{ + return param_format(*value); +} + + +template +inline constexpr bool is_unquoted_safe>{ + is_unquoted_safe}; + + +template struct nullness> +{ + static constexpr bool has_null = true; + static constexpr bool always_null = false; + static constexpr bool is_null(std::shared_ptr const &t) noexcept + { + return not t or pqxx::is_null(*t); + } + static constexpr std::shared_ptr null() { return {}; } +}; + + +template struct string_traits> +{ + static std::shared_ptr from_string(std::string_view text) + { + return std::make_shared(string_traits::from_string(text)); + } + + static zview to_buf(char *begin, char *end, std::shared_ptr const &value) + { + return string_traits::to_buf(begin, end, *value); + } + static char * + into_buf(char *begin, char *end, std::shared_ptr const &value) + { + return string_traits::into_buf(begin, end, *value); + } + static std::size_t size_buffer(std::shared_ptr const &value) noexcept + { + return pqxx::size_buffer(*value); + } +}; + + +template format param_format(std::shared_ptr const &value) +{ + return param_format(*value); +} + + +template +inline constexpr bool is_unquoted_safe>{ + is_unquoted_safe}; + + +template<> +struct nullness> + : no_null> +{}; + + +#if defined(PQXX_HAVE_CONCEPTS) +template struct nullness : no_null +{}; + + +template inline constexpr format param_format(DATA const &) +{ + return format::binary; +} + + +template struct string_traits +{ + static std::size_t size_buffer(DATA const &value) noexcept + { + return internal::size_esc_bin(std::size(value)); + } + + static zview to_buf(char *begin, char *end, DATA const &value) + { + return generic_to_buf(begin, end, value); + } + + static char *into_buf(char *begin, char *end, DATA const &value) + { + auto const budget{size_buffer(value)}; + if (internal::cmp_less(end - begin, budget)) + throw conversion_overrun{ + "Not enough buffer space to escape binary data."}; + internal::esc_bin(value, begin); + return begin + budget; + } + + static DATA from_string(std::string_view text) + { + auto const size{pqxx::internal::size_unesc_bin(std::size(text))}; + std::basic_string buf; + buf.resize(size); + pqxx::internal::unesc_bin(text, reinterpret_cast(buf.data())); + return buf; + } +}; +#endif // PQXX_HAVE_CONCEPTS + + +template<> struct string_traits> +{ + static std::size_t + size_buffer(std::basic_string const &value) noexcept + { + return internal::size_esc_bin(std::size(value)); + } + + static zview + to_buf(char *begin, char *end, std::basic_string const &value) + { + return generic_to_buf(begin, end, value); + } + + static char * + into_buf(char *begin, char *end, std::basic_string const &value) + { + auto const budget{size_buffer(value)}; + if (internal::cmp_less(end - begin, budget)) + throw conversion_overrun{ + "Not enough buffer space to escape binary data."}; + internal::esc_bin(value, begin); + return begin + budget; + } + + static std::basic_string from_string(std::string_view text) + { + auto const size{pqxx::internal::size_unesc_bin(std::size(text))}; + std::basic_string buf; + buf.resize(size); + pqxx::internal::unesc_bin(text, reinterpret_cast(buf.data())); + return buf; + } +}; + + +template<> +inline constexpr format param_format(std::basic_string const &) +{ + return format::binary; +} + + +template<> +struct nullness> + : no_null> +{}; + + +template<> struct string_traits> +{ + static std::size_t + size_buffer(std::basic_string_view const &value) noexcept + { + return internal::size_esc_bin(std::size(value)); + } + + static zview to_buf( + char *begin, char *end, std::basic_string_view const &value) + { + return generic_to_buf(begin, end, value); + } + + static char *into_buf( + char *begin, char *end, std::basic_string_view const &value) + { + auto const budget{size_buffer(value)}; + if (internal::cmp_less(end - begin, budget)) + throw conversion_overrun{ + "Not enough buffer space to escape binary data."}; + internal::esc_bin(value, begin); + return begin + budget; + } + + // There's no from_string, because there's nobody to hold the data. +}; + +template<> +inline constexpr format param_format(std::basic_string_view const &) +{ + return format::binary; +} +} // namespace pqxx + + +namespace pqxx::internal +{ +/// String traits for SQL arrays. +template struct array_string_traits +{ +private: + using elt_type = strip_t>; + using elt_traits = string_traits; + static constexpr zview s_null{"NULL"}; + +public: + static zview to_buf(char *begin, char *end, Container const &value) + { + return generic_to_buf(begin, end, value); + } + + static char *into_buf(char *begin, char *end, Container const &value) + { + std::size_t const budget{size_buffer(value)}; + if (internal::cmp_less(end - begin, budget)) + throw conversion_overrun{ + "Not enough buffer space to convert array to string."}; + + char *here = begin; + *here++ = '{'; + + bool nonempty{false}; + for (auto const &elt : value) + { + if (is_null(elt)) + { + s_null.copy(here, std::size(s_null)); + here += std::size(s_null); + } + else if constexpr (is_sql_array) + { + // Render nested array in-place. Then erase the trailing zero. + here = elt_traits::into_buf(here, end, elt) - 1; + } + else if constexpr (is_unquoted_safe) + { + // No need to quote or escape. Just convert the value straight into + // its place in the array, and "backspace" the trailing zero. + here = elt_traits::into_buf(here, end, elt) - 1; + } + else + { + *here++ = '"'; + + // Use the tail end of the destination buffer as an intermediate + // buffer. + auto const elt_budget{pqxx::size_buffer(elt)}; + for (char const c : elt_traits::to_buf(end - elt_budget, end, elt)) + { + if (c == '\\' or c == '"') + *here++ = '\\'; + *here++ = c; + } + *here++ = '"'; + } + *here++ = array_separator; + nonempty = true; + } + + // Erase that last comma, if present. + if (nonempty) + here--; + + *here++ = '}'; + *here++ = '\0'; + + return here; + } + + static std::size_t size_buffer(Container const &value) noexcept + { + if constexpr (is_unquoted_safe) + return 3 + std::accumulate( + std::begin(value), std::end(value), std::size_t{}, + [](std::size_t acc, elt_type const &elt) { + return acc + + (pqxx::is_null(elt) ? + std::size(s_null) : + elt_traits::size_buffer(elt)) - + 1; + }); + else + return 3 + std::accumulate( + std::begin(value), std::end(value), std::size_t{}, + [](std::size_t acc, elt_type const &elt) { + // Opening and closing quotes, plus worst-case escaping, + // but don't count the trailing zeroes. + std::size_t const elt_size{ + pqxx::is_null(elt) ? std::size(s_null) : + elt_traits::size_buffer(elt) - 1}; + return acc + 2 * elt_size + 2; + }); + } + + // We don't yet support parsing of array types using from_string. Doing so + // would require a reference to the connection. +}; +} // namespace pqxx::internal + + +namespace pqxx +{ +template +struct nullness> : no_null> +{}; + + +template +struct string_traits> + : internal::array_string_traits> +{}; + + +/// We don't know how to pass array params in binary format, so pass as text. +template +inline constexpr format param_format(std::vector const &) +{ + return format::text; +} + + +/// A `std::vector` is a binary string. Other vectors are not. +template +inline constexpr format param_format(std::vector const &) +{ + return format::binary; +} + + +template inline constexpr bool is_sql_array>{true}; + + +template +struct nullness> : no_null> +{}; + + +template +struct string_traits> + : internal::array_string_traits> +{}; + + +/// We don't know how to pass array params in binary format, so pass as text. +template +inline constexpr format param_format(std::array const &) +{ + return format::text; +} + + +/// An array of `std::byte` is a binary string. +template +inline constexpr format param_format(std::array const &) +{ + return format::binary; +} + + +template +inline constexpr bool is_sql_array>{true}; +} // namespace pqxx + + +namespace pqxx +{ +template inline std::string to_string(T const &value) +{ + if (is_null(value)) + throw conversion_error{ + "Attempt to convert null " + type_name + " to a string."}; + + std::string buf; + // We can't just reserve() space; modifying the terminating zero leads to + // undefined behaviour. + buf.resize(size_buffer(value)); + auto const data{buf.data()}; + auto const end{ + string_traits::into_buf(data, data + std::size(buf), value)}; + buf.resize(static_cast(end - data - 1)); + return buf; +} + + +template<> inline std::string to_string(float const &value) +{ + return internal::to_string_float(value); +} +template<> inline std::string to_string(double const &value) +{ + return internal::to_string_float(value); +} +template<> inline std::string to_string(long double const &value) +{ + return internal::to_string_float(value); +} +template<> inline std::string to_string(std::stringstream const &value) +{ + return value.str(); +} + + +template inline void into_string(T const &value, std::string &out) +{ + if (is_null(value)) + throw conversion_error{ + "Attempt to convert null " + type_name + " to a string."}; + + // We can't just reserve() data; modifying the terminating zero leads to + // undefined behaviour. + out.resize(size_buffer(value) + 1); + auto const data{out.data()}; + auto const end{ + string_traits::into_buf(data, data + std::size(out), value)}; + out.resize(static_cast(end - data - 1)); +} +} // namespace pqxx diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/encoding_group.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/encoding_group.hxx new file mode 100644 index 000000000..e17736e5b --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/encoding_group.hxx @@ -0,0 +1,60 @@ +/** Enum type for supporting encodings in libpqxx + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ENCODING_GROUP +#define PQXX_H_ENCODING_GROUP + +#include + +namespace pqxx::internal +{ +// Types of encodings supported by PostgreSQL, see +// https://www.postgresql.org/docs/current/static/multibyte.html#CHARSET-TABLE +enum class encoding_group +{ + // Handles all single-byte fixed-width encodings + MONOBYTE, + + // Multibyte encodings. + // Many of these can embed ASCII-like bytes inside multibyte characters, + // notably Big5, SJIS, SHIFT_JIS_2004, GP18030, GBK, JOHAB, UHC. + BIG5, + EUC_CN, + // TODO: Merge EUC_JP and EUC_JIS_2004? + EUC_JP, + EUC_JIS_2004, + EUC_KR, + EUC_TW, + GB18030, + GBK, + JOHAB, + MULE_INTERNAL, + // TODO: Merge SJIS and SHIFT_JIS_2004? + SJIS, + SHIFT_JIS_2004, + UHC, + UTF8, +}; + + +// TODO:: Can we just use string_view now? +/// Function type: "find the end of the current glyph." +/** This type of function takes a text buffer, and a location in that buffer, + * and returns the location one byte past the end of the current glyph. + * + * The start offset marks the beginning of the current glyph. It must fall + * within the buffer. + * + * There are multiple different glyph scanner implementations, for different + * kinds of encodings. + */ +using glyph_scanner_func = + std::size_t(char const buffer[], std::size_t buffer_len, std::size_t start); +} // namespace pqxx::internal + +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/encodings.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/encodings.hxx new file mode 100644 index 000000000..ba7fecc70 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/encodings.hxx @@ -0,0 +1,90 @@ +/** Internal string encodings support for libpqxx + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ENCODINGS +#define PQXX_H_ENCODINGS + +#include "pqxx/internal/encoding_group.hxx" + +#include +#include + + +namespace pqxx::internal +{ +char const *name_encoding(int encoding_id); + +/// Convert libpq encoding enum or encoding name to its libpqxx group. +encoding_group enc_group(int /* libpq encoding ID */); +encoding_group enc_group(std::string_view); + + +/// Look up the glyph scanner function for a given encoding group. +/** To identify the glyph boundaries in a buffer, call this to obtain the + * scanner function appropriate for the buffer's encoding. Then, repeatedly + * call the scanner function to find the glyphs. + */ +PQXX_LIBEXPORT glyph_scanner_func *get_glyph_scanner(encoding_group); + + +// TODO: For ASCII search, treat UTF8/EUC_*/MULE_INTERNAL as MONOBYTE. + +/// Find any of the ASCII characters `NEEDLE` in `haystack`. +/** Scans through `haystack` until it finds a single-byte character that + * matches any value in `NEEDLE`. + * + * If it finds one, returns its offset. If not, returns the end of the + * haystack. + */ +template +inline std::size_t find_char( + glyph_scanner_func *scanner, std::string_view haystack, + std::size_t here = 0u) +{ + auto const sz{std::size(haystack)}; + auto const data{std::data(haystack)}; + while (here < sz) + { + auto next{scanner(data, sz, here)}; + // (For some reason gcc had a problem with a right-fold here. But clang + // was fine.) + if ((... or (data[here] == NEEDLE))) + { + // Also check against a multibyte character starting with a bytes which + // just happens to match one of the ASCII bytes we're looking for. It'd + // be cleaner to check that first, but either works. So, let's apply the + // most selective filter first and skip this check in almost all cases. + if (next == here + 1) + return here; + } + + // Nope, no hit. Move on. + here = next; + } + return sz; +} + + +/// Iterate over the glyphs in a buffer. +/** Scans the glyphs in the buffer, and for each, passes its begin and its + * one-past-end pointers to `callback`. + */ +template +inline void for_glyphs( + encoding_group enc, CALLABLE callback, char const buffer[], + std::size_t buffer_len, std::size_t start = 0) +{ + auto const scan{get_glyph_scanner(enc)}; + for (std::size_t here = start, next; here < buffer_len; here = next) + { + next = scan(buffer, buffer_len, here); + callback(buffer + here, buffer + next); + } +} +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-errorhandler.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-errorhandler.hxx new file mode 100644 index 000000000..ffc12a6cf --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-errorhandler.hxx @@ -0,0 +1,26 @@ +#include + +namespace pqxx +{ +class connection; +class errorhandler; +} // namespace pqxx + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_errorhandler : callgate +{ + friend class pqxx::errorhandler; + + connection_errorhandler(reference x) : super(x) {} + + void register_errorhandler(errorhandler *h) + { + home().register_errorhandler(h); + } + void unregister_errorhandler(errorhandler *h) + { + home().unregister_errorhandler(h); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-largeobject.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-largeobject.hxx new file mode 100644 index 000000000..49feaf9e6 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-largeobject.hxx @@ -0,0 +1,35 @@ +#include + +#include +#include + +namespace pqxx +{ +class blob; +class largeobject; +} // namespace pqxx + + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_largeobject : callgate +{ + friend class pqxx::blob; + friend class pqxx::largeobject; + + connection_largeobject(reference x) : super(x) {} + + pq::PGconn *raw_connection() const { return home().raw_connection(); } +}; + + +class PQXX_PRIVATE const_connection_largeobject : callgate +{ + friend class pqxx::blob; + friend class pqxx::largeobject; + + const_connection_largeobject(reference x) : super(x) {} + + std::string error_message() const { return home().err_msg(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-notification_receiver.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-notification_receiver.hxx new file mode 100644 index 000000000..0bcb2db17 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-notification_receiver.hxx @@ -0,0 +1,29 @@ +#include + +#include "pqxx/connection.hxx" + + +namespace pqxx +{ +class notification_receiver; +} + + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_notification_receiver : callgate +{ + friend class pqxx::notification_receiver; + + connection_notification_receiver(reference x) : super(x) {} + + void add_receiver(notification_receiver *receiver) + { + home().add_receiver(receiver); + } + void remove_receiver(notification_receiver *receiver) noexcept + { + home().remove_receiver(receiver); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-pipeline.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-pipeline.hxx new file mode 100644 index 000000000..c6ae6e17a --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-pipeline.hxx @@ -0,0 +1,23 @@ +#include "pqxx/internal/libpq-forward.hxx" +#include + +#include "pqxx/pipeline.hxx" + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_pipeline : callgate +{ + friend class pqxx::pipeline; + + connection_pipeline(reference x) : super(x) {} + + void start_exec(char const query[]) { home().start_exec(query); } + pqxx::internal::pq::PGresult *get_result() { return home().get_result(); } + void cancel_query() { home().cancel_query(); } + + bool consume_input() noexcept { return home().consume_input(); } + bool is_busy() const noexcept { return home().is_busy(); } + + int encoding_id() { return home().encoding_id(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-sql_cursor.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-sql_cursor.hxx new file mode 100644 index 000000000..51a889844 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-sql_cursor.hxx @@ -0,0 +1,19 @@ +#include + +namespace pqxx::internal +{ +class sql_cursor; +} + + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_sql_cursor : callgate +{ + friend class pqxx::internal::sql_cursor; + + connection_sql_cursor(reference x) : super(x) {} + + result exec(char const query[]) { return home().exec(query); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-stream_from.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-stream_from.hxx new file mode 100644 index 000000000..8961e7146 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-stream_from.hxx @@ -0,0 +1,15 @@ +#include + +#include "pqxx/connection.hxx" + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_stream_from : callgate +{ + friend class pqxx::stream_from; + + connection_stream_from(reference x) : super{x} {} + + auto read_copy_line() { return home().read_copy_line(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-stream_to.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-stream_to.hxx new file mode 100644 index 000000000..a6974fb21 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-stream_to.hxx @@ -0,0 +1,17 @@ +#include + +#include "pqxx/stream_to.hxx" + + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_stream_to : callgate +{ + friend class pqxx::stream_to; + + connection_stream_to(reference x) : super(x) {} + + void write_copy_line(std::string_view line) { home().write_copy_line(line); } + void end_copy_write() { home().end_copy_write(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-transaction.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-transaction.hxx new file mode 100644 index 000000000..74d659253 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/connection-transaction.hxx @@ -0,0 +1,44 @@ +#include + +namespace pqxx +{ +class connection; +} + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE connection_transaction : callgate +{ + friend class pqxx::transaction_base; + + connection_transaction(reference x) : super(x) {} + + template result exec(STRING query, std::string_view desc) + { + return home().exec(query, desc); + } + + void register_transaction(transaction_base *t) + { + home().register_transaction(t); + } + void unregister_transaction(transaction_base *t) noexcept + { + home().unregister_transaction(t); + } + + auto read_copy_line() { return home().read_copy_line(); } + void write_copy_line(std::string_view line) { home().write_copy_line(line); } + void end_copy_write() { home().end_copy_write(); } + + result exec_prepared(zview statement, internal::c_params const &args) + { + return home().exec_prepared(statement, args); + } + + result exec_params(zview query, internal::c_params const &args) + { + return home().exec_params(query, args); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/errorhandler-connection.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/errorhandler-connection.hxx new file mode 100644 index 000000000..5560cedec --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/errorhandler-connection.hxx @@ -0,0 +1,13 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE errorhandler_connection : callgate +{ + friend class pqxx::connection; + + errorhandler_connection(reference x) : super(x) {} + + void unregister() noexcept { home().unregister(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/icursor_iterator-icursorstream.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/icursor_iterator-icursorstream.hxx new file mode 100644 index 000000000..296d22145 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/icursor_iterator-icursorstream.hxx @@ -0,0 +1,24 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE icursor_iterator_icursorstream : callgate +{ + friend class pqxx::icursorstream; + + icursor_iterator_icursorstream(reference x) : super(x) {} + + icursor_iterator::difference_type pos() const noexcept + { + return home().pos(); + } + + icursor_iterator *get_prev() { return home().m_prev; } + void set_prev(icursor_iterator *i) { home().m_prev = i; } + + icursor_iterator *get_next() { return home().m_next; } + void set_next(icursor_iterator *i) { home().m_next = i; } + + void fill(result const &r) { home().fill(r); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/icursorstream-icursor_iterator.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/icursorstream-icursor_iterator.hxx new file mode 100644 index 000000000..56056d5ef --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/icursorstream-icursor_iterator.hxx @@ -0,0 +1,32 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE icursorstream_icursor_iterator : callgate +{ + friend class pqxx::icursor_iterator; + + icursorstream_icursor_iterator(reference x) : super(x) {} + + void insert_iterator(icursor_iterator *i) noexcept + { + home().insert_iterator(i); + } + + void remove_iterator(icursor_iterator *i) const noexcept + { + home().remove_iterator(i); + } + + icursorstream::size_type forward() { return home().forward(); } + icursorstream::size_type forward(icursorstream::size_type n) + { + return home().forward(n); + } + + void service_iterators(icursorstream::difference_type p) + { + home().service_iterators(p); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-connection.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-connection.hxx new file mode 100644 index 000000000..daa0808c0 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-connection.hxx @@ -0,0 +1,14 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE result_connection : callgate +{ + friend class pqxx::connection; + + result_connection(reference x) : super(x) {} + + operator bool() const { return bool(home()); } + bool operator!() const { return not home(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-creation.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-creation.hxx new file mode 100644 index 000000000..3d9205f2c --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-creation.hxx @@ -0,0 +1,24 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE result_creation : callgate +{ + friend class pqxx::connection; + friend class pqxx::pipeline; + + result_creation(reference x) : super(x) {} + + static result create( + internal::pq::PGresult *rhs, std::shared_ptr const &query, + encoding_group enc) + { + return result(rhs, query, enc); + } + + void check_status(std::string_view desc = ""sv) const + { + return home().check_status(desc); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-pipeline.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-pipeline.hxx new file mode 100644 index 000000000..3ebe436d2 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-pipeline.hxx @@ -0,0 +1,16 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE result_pipeline : callgate +{ + friend class pqxx::pipeline; + + result_pipeline(reference x) : super(x) {} + + std::shared_ptr query_ptr() const + { + return home().query_ptr(); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-sql_cursor.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-sql_cursor.hxx new file mode 100644 index 000000000..78b450739 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/result-sql_cursor.hxx @@ -0,0 +1,13 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE result_sql_cursor : callgate +{ + friend class pqxx::internal::sql_cursor; + + result_sql_cursor(reference x) : super(x) {} + + char const *cmd_status() const noexcept { return home().cmd_status(); } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/transaction-sql_cursor.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/transaction-sql_cursor.hxx new file mode 100644 index 000000000..4ed78dc93 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/transaction-sql_cursor.hxx @@ -0,0 +1,10 @@ +#include + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE transaction_sql_cursor : callgate +{ + friend class pqxx::internal::sql_cursor; + transaction_sql_cursor(reference x) : super(x) {} +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/transaction-transaction_focus.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/transaction-transaction_focus.hxx new file mode 100644 index 000000000..ca7939a99 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/gates/transaction-transaction_focus.hxx @@ -0,0 +1,30 @@ +#include + +#include "pqxx/transaction_base.hxx" + +namespace pqxx::internal::gate +{ +class PQXX_PRIVATE transaction_transaction_focus : callgate +{ + friend class pqxx::transaction_focus; + + transaction_transaction_focus(reference x) : super(x) {} + + void register_focus(transaction_focus *focus) + { + home().register_focus(focus); + } + void unregister_focus(transaction_focus *focus) noexcept + { + home().unregister_focus(focus); + } + void register_pending_error(zview error) + { + home().register_pending_error(error); + } + void register_pending_error(std::string &&error) + { + home().register_pending_error(std::move(error)); + } +}; +} // namespace pqxx::internal::gate diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/header-post.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/header-post.hxx new file mode 100644 index 000000000..ff6bf8986 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/header-post.hxx @@ -0,0 +1,22 @@ +/* Compiler deficiency workarounds for compiling libpqxx headers. + * + * To be included at the end of each libpqxx header, in order to restore the + * client program's settings. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +// NO GUARDS HERE! This code should be executed every time! + +#if defined(_MSC_VER) +# pragma warning(pop) // Restore compiler's warning state +#endif + +#if !defined(PQXX_HEADER_PRE) +# error "Include pqxx/internal/header-post.hxx AFTER its 'pre' counterpart." +#endif + +#undef PQXX_HEADER_PRE diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/header-pre.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/header-pre.hxx new file mode 100644 index 000000000..abc1a398d --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/header-pre.hxx @@ -0,0 +1,169 @@ +/* Compiler settings for compiling libpqxx headers, and workarounds for all. + * + * Include this before including any other libpqxx headers from within libpqxx. + * And to balance it out, also include header-post.hxx at the end of the batch + * of headers. + * + * The public libpqxx headers (e.g. ``) include this already; + * there's no need to do this from within an application. + * + * Include this file at the highest aggregation level possible to avoid nesting + * and to keep things simple. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ + +// NO GUARD HERE! This part should be included every time this file is. +#if defined(_MSC_VER) + +// Save compiler's warning state, and set warning level 4 for maximum +// sensitivity to warnings. +# pragma warning(push, 4) + +// Visual C++ generates some entirely unreasonable warnings. Disable them. +// Copy constructor could not be generated. +# pragma warning(disable : 4511) +// Assignment operator could not be generated. +# pragma warning(disable : 4512) +// Can't expose outside classes without exporting them. Except the MSVC docs +// say please ignore the warning if it's a standard library class. +# pragma warning(disable : 4251) +// Can't derive library classes from outside classes without exporting them. +// Except the MSVC docs say please ignore the warning if the parent class is +// in the standard library. +# pragma warning(disable : 4275) +// Can't inherit from non-exported class. +# pragma warning(disable : 4275) + +#endif // _MSC_VER + + +#if defined(PQXX_HEADER_PRE) +# error "Avoid nesting #include of pqxx/internal/header-pre.hxx." +#endif + +#define PQXX_HEADER_PRE + + +// Workarounds & definitions that need to be included even in library's headers +#include "pqxx/config-public-compiler.h" + +// Enable ISO-646 alternative operaotr representations: "and" instead of "&&" +// etc. on older compilers. C++20 removes this header. +#if __has_include() +# include +#endif + + +#if defined(PQXX_HAVE_GCC_PURE) +/// Declare function "pure": no side effects, only reads globals and its args. +# define PQXX_PURE __attribute__((pure)) +#else +# define PQXX_PURE /* pure */ +#endif + + +#if defined(__GNUC__) +/// Tell the compiler to optimise a function for size, not speed. +# define PQXX_COLD __attribute__((cold)) +#else +# define PQXX_COLD /* cold */ +#endif + + +// Workarounds for Windows +#ifdef _WIN32 + +/* For now, export DLL symbols if _DLL is defined. This is done automatically + * by the compiler when linking to the dynamic version of the runtime library, + * according to "gzh" + */ +# if defined(PQXX_SHARED) && !defined(PQXX_LIBEXPORT) +# define PQXX_LIBEXPORT __declspec(dllimport) +# endif // PQXX_SHARED && !PQXX_LIBEXPORT + + +// Workarounds for Microsoft Visual C++ +# ifdef _MSC_VER + +// Suppress vtables on abstract classes. +# define PQXX_NOVTABLE __declspec(novtable) + +// Automatically link with the appropriate libpq (static or dynamic, debug or +// release). The default is to use the release DLL. Define PQXX_PQ_STATIC to +// link to a static version of libpq, and _DEBUG to link to a debug version. +// The two may be combined. +# if defined(PQXX_AUTOLINK) +# if defined(PQXX_PQ_STATIC) +# ifdef _DEBUG +# pragma comment(lib, "libpqd") +# else +# pragma comment(lib, "libpq") +# endif +# else +# ifdef _DEBUG +# pragma comment(lib, "libpqddll") +# else +# pragma comment(lib, "libpqdll") +# endif +# endif +# endif + +// If we're not compiling libpqxx itself, automatically link with the +// appropriate libpqxx library. To link with the libpqxx DLL, define +// PQXX_SHARED; the default is to link with the static library. A static link +// is the recommended practice. +// +// The preprocessor macro PQXX_INTERNAL is used to detect whether we +// are compiling the libpqxx library itself. When you compile the library +// yourself using your own project file, make sure to include this macro. +# if defined(PQXX_AUTOLINK) && !defined(PQXX_INTERNAL) +# ifdef PQXX_SHARED +# ifdef _DEBUG +# pragma comment(lib, "libpqxxD") +# else +# pragma comment(lib, "libpqxx") +# endif +# else // !PQXX_SHARED +# ifdef _DEBUG +# pragma comment(lib, "libpqxx_staticD") +# else +# pragma comment(lib, "libpqxx_static") +# endif +# endif +# endif + +# endif // _MSC_VER + +#elif defined(PQXX_HAVE_GCC_VISIBILITY) // !_WIN32 + +# define PQXX_LIBEXPORT __attribute__((visibility("default"))) +# define PQXX_PRIVATE __attribute__((visibility("hidden"))) + +#endif // PQXX_HAVE_GCC_VISIBILITY + + +#ifndef PQXX_LIBEXPORT +# define PQXX_LIBEXPORT /* libexport */ +#endif + +#ifndef PQXX_PRIVATE +# define PQXX_PRIVATE /* private */ +#endif + +#ifndef PQXX_NOVTABLE +# define PQXX_NOVTABLE /* novtable */ +#endif + +// C++20: Assume support. +#if defined(PQXX_HAVE_LIKELY) +# define PQXX_LIKELY [[likely]] +# define PQXX_UNLIKELY [[unlikely]] +#else +# define PQXX_LIKELY /* [[likely]] */ +# define PQXX_UNLIKELY /* [[unlikely]] */ +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/ignore-deprecated-post.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/ignore-deprecated-post.hxx new file mode 100644 index 000000000..cebcf0594 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/ignore-deprecated-post.hxx @@ -0,0 +1,15 @@ +/// End a code block started by "ignore-deprecated-pre.hxx". + +#if !defined(PQXX_IGNORING_DEPRECATED) +# error "Ended an 'ignore-deprecated' block while none was active." +#endif + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif // __GNUC__ + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#undef PQXX_IGNORING_DEPRECATED diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/ignore-deprecated-pre.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/ignore-deprecated-pre.hxx new file mode 100644 index 000000000..8ac57afaa --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/ignore-deprecated-pre.hxx @@ -0,0 +1,28 @@ +/** Start a block of deprecated code which may call other deprecated code. + * + * Most compilers will emit warnings when deprecated code is invoked from + * non-deprecated code. But some compilers (notably gcc) will always emit the + * warning even when the calling code is also deprecated. + * + * This header starts a block where those warnings are suppressed. It can be + * included inside a code block. + * + * Always match the #include with a closing #include of + * "ignore-deprecated-post.hxx". To avoid mistakes, keep the enclosed area as + * small as possible. + */ +#if defined(PQXX_IGNORING_DEPRECATED) +# error "Started an 'ignore-deprecated' block inside another." +#endif + +#define PQXX_IGNORING_DEPRECATED + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif // __GNUC__ + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4996) +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/libpq-forward.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/libpq-forward.hxx new file mode 100644 index 000000000..9e74f79ec --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/libpq-forward.hxx @@ -0,0 +1,31 @@ +/** Minimal forward declarations of libpq types needed in libpqxx headers. + * + * DO NOT INCLUDE THIS FILE when building client programs. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +extern "C" +{ + struct pg_conn; + struct pg_result; + struct pgNotify; +} + +/// Forward declarations of libpq types as needed in libpqxx headers. +namespace pqxx::internal::pq +{ +using PGconn = pg_conn; +using PGresult = pg_result; +using PGnotify = pgNotify; +using PQnoticeProcessor = void (*)(void *, char const *); +} // namespace pqxx::internal::pq + +namespace pqxx +{ +/// PostgreSQL database row identifier. +using oid = unsigned int; +} // namespace pqxx diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/result_iter.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/result_iter.hxx new file mode 100644 index 000000000..1fa1f7d8a --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/result_iter.hxx @@ -0,0 +1,124 @@ +/** Result loops. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_RESULT_ITER +#define PQXX_H_RESULT_ITER + +#include + +#include "pqxx/strconv.hxx" + +namespace pqxx +{ +class result; +} // namespace pqxx + + +namespace pqxx::internal +{ +// C++20: Replace with generator? +/// Iterator for looped unpacking of a result. +template class result_iter +{ +public: + using value_type = std::tuple; + + /// Construct an "end" iterator. + result_iter() = default; + + explicit result_iter(result const &home) : + m_home{&home}, m_size{std::size(home)} + { + if (not std::empty(home)) + read(); + } + result_iter(result_iter const &) = default; + + result_iter &operator++() + { + m_index++; + if (m_index >= m_size) + m_home = nullptr; + else + read(); + return *this; + } + + /// Comparison only works for comparing to end(). + bool operator==(result_iter const &rhs) const + { + return m_home == rhs.m_home; + } + bool operator!=(result_iter const &rhs) const { return not(*this == rhs); } + + value_type const &operator*() const { return m_value; } + +private: + void read() { (*m_home)[m_index].convert(m_value); } + + result const *m_home{nullptr}; + result::size_type m_index{0}; + result::size_type m_size; + value_type m_value; +}; + + +template class result_iteration +{ +public: + using iterator = result_iter; + explicit result_iteration(result const &home) : m_home{home} + { + constexpr auto tup_size{sizeof...(TYPE)}; + if (home.columns() != tup_size) + throw usage_error{internal::concat( + "Tried to extract ", to_string(tup_size), + " field(s) from a result with ", to_string(home.columns()), + " column(s).")}; + } + iterator begin() const + { + if (std::size(m_home) == 0) + return end(); + else + return iterator{m_home}; + } + iterator end() const { return {}; } + +private: + pqxx::result const &m_home; +}; +} // namespace pqxx::internal + + +template inline auto pqxx::result::iter() const +{ + return pqxx::internal::result_iteration{*this}; +} + + +template +inline void pqxx::result::for_each(CALLABLE &&func) const +{ + using args_tuple = internal::args_t; + constexpr auto sz{std::tuple_size_v}; + static_assert( + sz > 0, + "Callback for for_each must take parameters, one for each column in the " + "result."); + + auto const cols{this->columns()}; + if (sz != cols) + throw usage_error{internal::concat( + "Callback to for_each takes ", sz, "parameter", (sz == 1) ? "" : "s", + ", but result set has ", cols, "field", (cols == 1) ? "" : "s", ".")}; + + using pass_tuple = pqxx::internal::strip_types_t; + for (auto const r : *this) std::apply(func, r.as_tuple()); +} +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/result_iterator.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/result_iterator.hxx new file mode 100644 index 000000000..3f27a1d3f --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/result_iterator.hxx @@ -0,0 +1,389 @@ +/* Definitions for the pqxx::result class and support classes. + * + * pqxx::result represents the set of result rows from a database query. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/result instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_RESULT_ITERATOR +#define PQXX_H_RESULT_ITERATOR + +#include "pqxx/row.hxx" + + +/* Result iterator. + * + * Don't include this header from your own application; it is included for you + * by other libpqxx headers. + */ + +namespace pqxx +{ +/// Iterator for rows in a result. Use as result::const_iterator. +/** A result, once obtained, cannot be modified. Therefore there is no + * plain iterator type for result. However its const_iterator type can be + * used to inspect its rows without changing them. + */ +class PQXX_LIBEXPORT const_result_iterator : public row +{ +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = row const; + using pointer = row const *; + using reference = row; + using size_type = result_size_type; + using difference_type = result_difference_type; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + /// Create an iterator, but in an unusable state. + const_result_iterator() noexcept = default; + /// Copy an iterator. + const_result_iterator(const_result_iterator const &) noexcept = default; + /// Move an iterator. + const_result_iterator(const_result_iterator &&) noexcept = default; + + /// Begin iterating a @ref row. + const_result_iterator(row const &t) noexcept : row{t} {} +#include "pqxx/internal/ignore-deprecated-post.hxx" + + /** + * @name Dereferencing operators + * + * An iterator "points to" its own row, which is also itself. This makes it + * easy to address a @ref result as a two-dimensional container, without + * going through the intermediate step of dereferencing the iterator. It + * makes the interface similar to C pointer/array semantics. + * + * IIRC Alex Stepanov, the inventor of the STL, once remarked that having + * this as standard behaviour for pointers would be useful in some + * algorithms. So even if this makes me look foolish, I would seem to be in + * distinguished company. + */ + //@{ + /// Dereference the iterator. + [[nodiscard]] pointer operator->() const { return this; } + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + /// Dereference the iterator. + [[nodiscard]] reference operator*() const { return *this; } +#include "pqxx/internal/ignore-deprecated-post.hxx" + //@} + + /** + * @name Field access + */ + //@{ + using row::back; + using row::front; + using row::operator[]; + using row::at; + using row::rownumber; + //@} + + /** + * @name Manipulations + */ + //@{ + const_result_iterator &operator=(const_result_iterator const &rhs) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + row::operator=(rhs); +#include "pqxx/internal/ignore-deprecated-post.hxx" + return *this; + } + + const_result_iterator &operator=(const_result_iterator &&rhs) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + row::operator=(std::move(rhs)); +#include "pqxx/internal/ignore-deprecated-post.hxx" + return *this; + } + + const_result_iterator operator++(int); + const_result_iterator &operator++() + { + ++m_index; + return *this; + } + const_result_iterator operator--(int); + const_result_iterator &operator--() + { + --m_index; + return *this; + } + + const_result_iterator &operator+=(difference_type i) + { + m_index += i; + return *this; + } + const_result_iterator &operator-=(difference_type i) + { + m_index -= i; + return *this; + } + + /// Interchange two iterators in an exception-safe manner. + void swap(const_result_iterator &other) noexcept + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + row::swap(other); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + //@} + + /** + * @name Comparisons + */ + //@{ + [[nodiscard]] bool operator==(const_result_iterator const &i) const + { + return m_index == i.m_index; + } + [[nodiscard]] bool operator!=(const_result_iterator const &i) const + { + return m_index != i.m_index; + } + [[nodiscard]] bool operator<(const_result_iterator const &i) const + { + return m_index < i.m_index; + } + [[nodiscard]] bool operator<=(const_result_iterator const &i) const + { + return m_index <= i.m_index; + } + [[nodiscard]] bool operator>(const_result_iterator const &i) const + { + return m_index > i.m_index; + } + [[nodiscard]] bool operator>=(const_result_iterator const &i) const + { + return m_index >= i.m_index; + } + //@} + + /** + * @name Arithmetic operators + */ + //@{ + [[nodiscard]] inline const_result_iterator operator+(difference_type) const; + friend const_result_iterator + operator+(difference_type, const_result_iterator const &); + [[nodiscard]] inline const_result_iterator operator-(difference_type) const; + [[nodiscard]] inline difference_type + operator-(const_result_iterator const &) const; + //@} + +private: + friend class pqxx::result; + const_result_iterator(pqxx::result const *r, result_size_type i) noexcept : + row{*r, i, r->columns()} + {} +}; + + +/// Reverse iterator for result. Use as result::const_reverse_iterator. +class PQXX_LIBEXPORT const_reverse_result_iterator + : private const_result_iterator +{ +public: + using super = const_result_iterator; + using iterator_type = const_result_iterator; + using iterator_type::difference_type; + using iterator_type::iterator_category; + using iterator_type::pointer; + using value_type = iterator_type::value_type; + using reference = iterator_type::reference; + + /// Create an iterator, but in an unusable state. + const_reverse_result_iterator() = default; + /// Copy an iterator. + const_reverse_result_iterator(const_reverse_result_iterator const &rhs) = + default; + /// Copy a reverse iterator from a regular iterator. + explicit const_reverse_result_iterator(const_result_iterator const &rhs) : + const_result_iterator{rhs} + { + super::operator--(); + } + + /// Move a regular iterator into a reverse iterator. + explicit const_reverse_result_iterator(const_result_iterator const &&rhs) : + const_result_iterator{std::move(rhs)} + { + super::operator--(); + } + + /// Return the underlying "regular" iterator (as per standard library). + [[nodiscard]] PQXX_PURE const_result_iterator base() const noexcept; + + /** + * @name Dereferencing operators + */ + //@{ + /// Dereference iterator. + using const_result_iterator::operator->; + /// Dereference iterator. + using const_result_iterator::operator*; + //@} + + /** + * @name Field access + */ + //@{ + using const_result_iterator::back; + using const_result_iterator::front; + using const_result_iterator::operator[]; + using const_result_iterator::at; + using const_result_iterator::rownumber; + //@} + + /** + * @name Manipulations + */ + //@{ + const_reverse_result_iterator & + operator=(const_reverse_result_iterator const &r) + { + iterator_type::operator=(r); + return *this; + } + const_reverse_result_iterator &operator=(const_reverse_result_iterator &&r) + { + iterator_type::operator=(std::move(r)); + return *this; + } + const_reverse_result_iterator &operator++() + { + iterator_type::operator--(); + return *this; + } + const_reverse_result_iterator operator++(int); + const_reverse_result_iterator &operator--() + { + iterator_type::operator++(); + return *this; + } + const_reverse_result_iterator operator--(int); + const_reverse_result_iterator &operator+=(difference_type i) + { + iterator_type::operator-=(i); + return *this; + } + const_reverse_result_iterator &operator-=(difference_type i) + { + iterator_type::operator+=(i); + return *this; + } + + void swap(const_reverse_result_iterator &other) noexcept + { + const_result_iterator::swap(other); + } + //@} + + /** + * @name Arithmetic operators + */ + //@{ + [[nodiscard]] const_reverse_result_iterator + operator+(difference_type i) const + { + return const_reverse_result_iterator(base() - i); + } + [[nodiscard]] const_reverse_result_iterator operator-(difference_type i) + { + return const_reverse_result_iterator(base() + i); + } + [[nodiscard]] difference_type + operator-(const_reverse_result_iterator const &rhs) const + { + return rhs.const_result_iterator::operator-(*this); + } + //@} + + /** + * @name Comparisons + */ + //@{ + [[nodiscard]] bool + operator==(const_reverse_result_iterator const &rhs) const noexcept + { + return iterator_type::operator==(rhs); + } + [[nodiscard]] bool + operator!=(const_reverse_result_iterator const &rhs) const noexcept + { + return not operator==(rhs); + } + + [[nodiscard]] bool operator<(const_reverse_result_iterator const &rhs) const + { + return iterator_type::operator>(rhs); + } + [[nodiscard]] bool operator<=(const_reverse_result_iterator const &rhs) const + { + return iterator_type::operator>=(rhs); + } + [[nodiscard]] bool operator>(const_reverse_result_iterator const &rhs) const + { + return iterator_type::operator<(rhs); + } + [[nodiscard]] bool operator>=(const_reverse_result_iterator const &rhs) const + { + return iterator_type::operator<=(rhs); + } + //@} +}; + + +inline const_result_iterator +const_result_iterator::operator+(result::difference_type o) const +{ + return {&m_result, size_type(result::difference_type(m_index) + o)}; +} + +inline const_result_iterator +operator+(result::difference_type o, const_result_iterator const &i) +{ + return i + o; +} + +inline const_result_iterator +const_result_iterator::operator-(result::difference_type o) const +{ + return {&m_result, result_size_type(result::difference_type(m_index) - o)}; +} + +inline result::difference_type +const_result_iterator::operator-(const const_result_iterator &i) const +{ + return result::difference_type(num() - i.num()); +} + +inline const_result_iterator result::end() const noexcept +{ + return {this, size()}; +} + + +inline const_result_iterator result::cend() const noexcept +{ + return end(); +} + + +inline const_reverse_result_iterator +operator+(result::difference_type n, const_reverse_result_iterator const &i) +{ + return const_reverse_result_iterator{i.base() - n}; +} + +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/sql_cursor.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/sql_cursor.hxx new file mode 100644 index 000000000..a26d06306 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/sql_cursor.hxx @@ -0,0 +1,118 @@ +/** Internal wrapper for SQL cursors. Supports higher-level cursor classes. + * + * DO NOT INCLUDE THIS FILE DIRECTLY. Other headers include it for you. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_SQL_CURSOR +#define PQXX_H_SQL_CURSOR + +namespace pqxx::internal +{ +/// Cursor with SQL positioning semantics. +/** Thin wrapper around an SQL cursor, with SQL's ideas of positioning. + * + * SQL cursors have pre-increment/pre-decrement semantics, with on either end + * of the result set a special position that does not repesent a row. This + * class models SQL cursors for the purpose of implementing more C++-like + * semantics on top. + * + * Positions of actual rows are numbered starting at 1. Position 0 exists but + * does not refer to a row. There is a similar non-row position at the end of + * the result set. + * + * Don't use this at home. You deserve better. Use the stateles_cursor + * instead. + */ +class PQXX_LIBEXPORT sql_cursor : public cursor_base +{ +public: + sql_cursor( + transaction_base &t, std::string_view query, std::string_view cname, + cursor_base::access_policy ap, cursor_base::update_policy up, + cursor_base::ownership_policy op, bool hold); + + sql_cursor( + transaction_base &t, std::string_view cname, + cursor_base::ownership_policy op); + + ~sql_cursor() noexcept { close(); } + + result fetch(difference_type rows, difference_type &displacement); + result fetch(difference_type rows) + { + difference_type d = 0; + return fetch(rows, d); + } + difference_type move(difference_type rows, difference_type &displacement); + difference_type move(difference_type rows) + { + difference_type d = 0; + return move(rows, d); + } + + /// Current position, or -1 for unknown + /** + * The starting position, just before the first row, counts as position zero. + * + * Position may be unknown if (and only if) this cursor was adopted, and has + * never hit its starting position (position zero). + */ + difference_type pos() const noexcept { return m_pos; } + + /// End position, or -1 for unknown + /** + * Returns the final position, just after the last row in the result set. The + * starting position, just before the first row, counts as position zero. + * + * End position is unknown until it is encountered during use. + */ + difference_type endpos() const noexcept { return m_endpos; } + + /// Return zero-row result for this cursor. + result const &empty_result() const noexcept { return m_empty_result; } + + void close() noexcept; + +private: + difference_type adjust(difference_type hoped, difference_type actual); + static std::string stridestring(difference_type); + /// Initialize cached empty result. Call only at beginning or end! + void init_empty_result(transaction_base &); + + /// Connection in which this cursor lives. + connection &m_home; + + /// Zero-row result from this cursor (or plain empty one if cursor is + /// adopted) + result m_empty_result; + + result m_cached_current_row; + + /// Is this cursor adopted (as opposed to created by this cursor object)? + bool m_adopted; + + /// Will this cursor object destroy its SQL cursor when it dies? + cursor_base::ownership_policy m_ownership; + + /// At starting position (-1), somewhere in the middle (0), or past end (1) + int m_at_end; + + /// Position, or -1 for unknown + difference_type m_pos; + + /// End position, or -1 for unknown + difference_type m_endpos = -1; +}; + + +PQXX_LIBEXPORT result_size_type obtain_stateless_cursor_size(sql_cursor &); +PQXX_LIBEXPORT result stateless_cursor_retrieve( + sql_cursor &, result::difference_type size, + result::difference_type begin_pos, result::difference_type end_pos); +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/statement_parameters.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/statement_parameters.hxx new file mode 100644 index 000000000..b078bf6e0 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/statement_parameters.hxx @@ -0,0 +1,131 @@ +/** Common implementation for statement parameter lists. + * + * These are used for both prepared statements and parameterized statements. + * + * DO NOT INCLUDE THIS FILE DIRECTLY. Other headers include it for you. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_STATEMENT_PARAMETER +#define PQXX_H_STATEMENT_PARAMETER + +#include +#include +#include +#include +#include + +#include "pqxx/binarystring.hxx" +#include "pqxx/strconv.hxx" +#include "pqxx/util.hxx" + + +namespace pqxx::internal +{ +template +constexpr inline auto const iterator_identity{ + [](decltype(*std::declval()) x) { return x; }}; + + +/// Marker type: pass a dynamically-determined number of statement parameters. +/** @deprecated Use @ref params instead. + * + * Normally when invoking a prepared or parameterised statement, the number + * of parameters is known at compile time. For instance, + * `t.exec_prepared("foo", 1, "x");` executes statement `foo` with two + * parameters, an `int` and a C string. + * + * But sometimes you may want to pass a number of parameters known only at run + * time. In those cases, a @ref dynamic_params encodes a dynamically + * determined number of parameters. You can mix these with regular, static + * parameter lists, and you can re-use them for multiple statement invocations. + * + * A dynamic_params object does not store copies of its parameters, so make + * sure they remain accessible until you've executed the statement. + * + * The ACCESSOR is an optional callable (such as a lambda). If you pass an + * accessor `a`, then each parameter `p` goes into your statement as `a(p)`. + */ +template)> +class dynamic_params +{ +public: + /// Wrap a sequence of pointers or iterators. + constexpr dynamic_params(IT begin, IT end) : + m_begin(begin), m_end(end), m_accessor(iterator_identity) + {} + + /// Wrap a sequence of pointers or iterators. + /** This version takes an accessor callable. If you pass an accessor `acc`, + * then any parameter `p` will go into the statement's parameter list as + * `acc(p)`. + */ + constexpr dynamic_params(IT begin, IT end, ACCESSOR &acc) : + m_begin(begin), m_end(end), m_accessor(acc) + {} + + /// Wrap a container. + template + explicit constexpr dynamic_params(C &container) : + dynamic_params(std::begin(container), std::end(container)) + {} + + /// Wrap a container. + /** This version takes an accessor callable. If you pass an accessor `acc`, + * then any parameter `p` will go into the statement's parameter list as + * `acc(p)`. + */ + template + explicit constexpr dynamic_params(C &container, ACCESSOR &acc) : + dynamic_params(std::begin(container), std::end(container), acc) + {} + + constexpr IT begin() const noexcept { return m_begin; } + constexpr IT end() const noexcept { return m_end; } + + constexpr auto access(decltype(*std::declval()) value) const + -> decltype(std::declval()(value)) + { + return m_accessor(value); + } + +private: + IT const m_begin, m_end; + ACCESSOR m_accessor = iterator_identity; +}; + + +/// Internal type: encode statement parameters. +/** Compiles arguments for prepared statements and parameterised queries into + * a format that can be passed into libpq. + * + * Objects of this type are meant to be short-lived: a `c_params` lives and + * dies entirely within the call to execute. So, for example, if you pass in a + * non-null pointer as a parameter, @ref params may simply use that pointer as + * a parameter value, without arranging longer-term storage for the data to + * which it points. All values referenced by parameters must remain "live" + * until the parameterised or prepared statement has been executed. + */ +struct PQXX_LIBEXPORT c_params +{ + c_params() = default; + /// Copying these objects is pointless and expensive. Don't do it. + c_params(c_params const &) = delete; + c_params(c_params &&) = default; + + /// Pre-allocate storage for `n` parameters. + void reserve(std::size_t n) &; + + /// As used by libpq: pointers to parameter values. + std::vector values; + /// As used by libpq: lengths of non-null arguments, in bytes. + std::vector lengths; + /// As used by libpq: effectively boolean "is this a binary parameter?" + std::vector formats; +}; +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/stream_iterator.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/stream_iterator.hxx new file mode 100644 index 000000000..f240dcfa7 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/stream_iterator.hxx @@ -0,0 +1,105 @@ +/** Stream iterators. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_STREAM_ITERATOR +#define PQXX_H_STREAM_ITERATOR + +#include + +namespace pqxx +{ +class stream_from; +} + + +namespace pqxx::internal +{ +// C++20: Replace with generator? +/// Input iterator for stream_from. +/** Just barely enough to support range-based "for" loops. Don't assume that + * any of the usual behaviour works beyond that. + */ +template class stream_input_iterator +{ +public: + using value_type = std::tuple; + + /// Construct an "end" iterator. + stream_input_iterator() = default; + + explicit stream_input_iterator(stream_from &home) : m_home(&home) + { + advance(); + } + stream_input_iterator(stream_input_iterator const &) = default; + + stream_input_iterator &operator++() + { + advance(); + return *this; + } + + value_type const &operator*() const { return m_value; } + + /// Comparison only works for comparing to end(). + bool operator==(stream_input_iterator const &rhs) const + { + return m_home == rhs.m_home; + } + /// Comparison only works for comparing to end(). + bool operator!=(stream_input_iterator const &rhs) const + { + return not(*this == rhs); + } + +private: + void advance() + { + if (m_home == nullptr) + throw usage_error{"Moving stream_from iterator beyond end()."}; + if (not((*m_home) >> m_value)) + m_home = nullptr; + } + + stream_from *m_home{nullptr}; + value_type m_value; +}; + + +// C++20: Replace with generator? +/// Iteration over a @ref stream_from. +template class stream_input_iteration +{ +public: + using iterator = stream_input_iterator; + explicit stream_input_iteration(stream_from &home) : m_home{home} {} + iterator begin() const { return iterator{m_home}; } + iterator end() const { return {}; } + +private: + stream_from &m_home; +}; + + +// C++20: Replace with generator? +/// Iteration over a @ref stream_from, deleting it once done. +template class owning_stream_input_iteration +{ +public: + using iterator = stream_input_iterator; + explicit owning_stream_input_iteration(std::unique_ptr &&home) : + m_home{std::move(home)} + {} + iterator begin() const { return iterator{*m_home.get()}; } + iterator end() const { return {}; } + +private: + std::unique_ptr m_home; +}; +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/wait.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/wait.hxx new file mode 100644 index 000000000..7a82e6553 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/internal/wait.hxx @@ -0,0 +1,18 @@ +#if !defined(PQXX_WAIT_HXX) +# define PQXX_WAIT_HXX + +namespace pqxx::internal +{ +/// Wait. +/** This is normally `std::this_thread::sleep_for()`. But MinGW's `thread` + * header doesn't work, so we must be careful about including it. + */ +void PQXX_LIBEXPORT wait_for(unsigned int microseconds); + + +/// Wait for a socket to be ready for reading/writing, or timeout. +PQXX_LIBEXPORT void wait_fd( + int fd, bool for_read, bool for_write, unsigned seconds = 1, + unsigned microseconds = 0); +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/isolation b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/isolation new file mode 100644 index 000000000..1b801329b --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/isolation @@ -0,0 +1,8 @@ +/** Transaction isolation levels. + * + * Policies and traits describing SQL transaction isolation levels + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/isolation.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/isolation.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/isolation.hxx new file mode 100644 index 000000000..0698c6ab4 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/isolation.hxx @@ -0,0 +1,75 @@ +/* Definitions for transaction isolation levels, and such. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/isolation instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ISOLATION +#define PQXX_H_ISOLATION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/util.hxx" + +namespace pqxx +{ +/// Should a transaction be read-only, or read-write? +/** No, this is not an isolation level. So it really doesn't belong here. + * But it's not really worth a separate header. + */ +enum class write_policy +{ + read_only, + read_write +}; + + +/// Transaction isolation levels. +/** These are as defined in the SQL standard. But there are a few notes + * specific to PostgreSQL. + * + * First, postgres does not support "read uncommitted." The lowest level you + * can get is "read committed," which is better. PostgreSQL is built on the + * MVCC paradigm, which guarantees "read committed" isolation without any + * additional performance overhead, so there was no point in providing the + * lower level. + * + * Second, "repeatable read" also makes more isolation guarantees than the + * standard requires. According to the standard, this level prevents "dirty + * reads" and "nonrepeatable reads," but not "phantom reads." In postgres, + * it actually prevents all three. + * + * Third, "serializable" is only properly supported starting at postgres 9.1. + * If you request "serializable" isolation on an older backend, you will get + * the same isolation as in "repeatable read." It's better than the + * "repeatable read" defined in the SQL standard, but not a complete + * implementation of the standard's "serializable" isolation level. + * + * In general, a lower isolation level will allow more surprising interactions + * between ongoing transactions, but improve performance. A higher level + * gives you more protection from subtle concurrency bugs, but sometimes it + * may not be possible to complete your transaction without avoiding paradoxes + * in the data. In that case a transaction may fail, and the application will + * have to re-do the whole thing based on the latest state of the database. + * (If you want to retry your code in that situation, have a look at the + * transactor framework.) + * + * Study the levels and design your application with the right level in mind. + */ +enum isolation_level +{ + // PostgreSQL only has the better isolation levels. + // read_uncommitted, + + read_committed, + repeatable_read, + serializable, +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/largeobject b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/largeobject new file mode 100644 index 000000000..1f2f94790 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/largeobject @@ -0,0 +1,8 @@ +/** Large Objects interface. + * + * Supports direct access to large objects, as well as through I/O streams + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/largeobject.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/largeobject.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/largeobject.hxx new file mode 100644 index 000000000..ebafc51d8 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/largeobject.hxx @@ -0,0 +1,735 @@ +/* Large Objects interface. Deprecated; use blob instead. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/largeobject instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_LARGEOBJECT +#define PQXX_H_LARGEOBJECT + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#include "pqxx/dbtransaction.hxx" + + +namespace pqxx +{ +/// Identity of a large object. +/** @deprecated Use the @ref blob class instead. + * + * Encapsulates the identity of a large object. + * + * A largeobject must be accessed only from within a backend transaction, but + * the object's identity remains valid as long as the object exists. + */ +class PQXX_LIBEXPORT largeobject +{ +public: + using size_type = large_object_size_type; + + /// Refer to a nonexistent large object (similar to what a null pointer + /// does). + [[deprecated("Use blob instead.")]] largeobject() noexcept = default; + + /// Create new large object. + /** @param t Backend transaction in which the object is to be created. + */ + [[deprecated("Use blob instead.")]] explicit largeobject(dbtransaction &t); + + /// Wrap object with given oid. + /** Convert combination of a transaction and object identifier into a + * large object identity. Does not affect the database. + * @param o Object identifier for the given object. + */ + [[deprecated("Use blob instead.")]] explicit largeobject(oid o) noexcept : + m_id{o} + {} + + /// Import large object from a local file. + /** Creates a large object containing the data found in the given file. + * @param t Backend transaction in which the large object is to be created. + * @param file A filename on the client program's filesystem. + */ + [[deprecated("Use blob instead.")]] largeobject( + dbtransaction &t, std::string_view file); + + /// Take identity of an opened large object. + /** Copy identity of already opened large object. Note that this may be done + * as an implicit conversion. + * @param o Already opened large object to copy identity from. + */ + [[deprecated("Use blob instead.")]] largeobject( + largeobjectaccess const &o) noexcept; + + /// Object identifier. + /** The number returned by this function identifies the large object in the + * database we're connected to (or oid_none is returned if we refer to the + * null object). + */ + [[nodiscard]] oid id() const noexcept { return m_id; } + + /** + * @name Identity comparisons + * + * These operators compare the object identifiers of large objects. This has + * nothing to do with the objects' actual contents; use them only for keeping + * track of containers of references to large objects and such. + */ + //@{ + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator==(largeobject const &other) const + { + return m_id == other.m_id; + } + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator!=(largeobject const &other) const + { + return m_id != other.m_id; + } + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator<=(largeobject const &other) const + { + return m_id <= other.m_id; + } + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator>=(largeobject const &other) const + { + return m_id >= other.m_id; + } + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator<(largeobject const &other) const + { + return m_id < other.m_id; + } + /// Compare object identities + /** @warning Only valid between large objects in the same database. */ + [[nodiscard]] bool operator>(largeobject const &other) const + { + return m_id > other.m_id; + } + //@} + + /// Export large object's contents to a local file + /** Writes the data stored in the large object to the given file. + * @param t Transaction in which the object is to be accessed + * @param file A filename on the client's filesystem + */ + void to_file(dbtransaction &t, std::string_view file) const; + + /// Delete large object from database + /** Unlike its low-level equivalent cunlink, this will throw an exception if + * deletion fails. + * @param t Transaction in which the object is to be deleted + */ + void remove(dbtransaction &t) const; + +protected: + PQXX_PURE static internal::pq::PGconn * + raw_connection(dbtransaction const &T); + + PQXX_PRIVATE std::string reason(connection const &, int err) const; + +private: + oid m_id = oid_none; +}; + + +/// Accessor for large object's contents. +/** @deprecated Use the `blob` class instead. + */ +class PQXX_LIBEXPORT largeobjectaccess : private largeobject +{ +public: + using largeobject::size_type; + using off_type = size_type; + using pos_type = size_type; + + /// Open mode: `in`, `out` (can be combined using "bitwise or"). + /** According to the C++ standard, these should be in `std::ios_base`. We + * take them from derived class `std::ios` instead, which is easier on the + * eyes. + * + * Historical note: taking it from std::ios was originally a workaround for a + * problem with gcc 2.95. + */ + using openmode = std::ios::openmode; + + /// Default open mode: in, out, binary. + static constexpr auto default_mode{ + std::ios::in | std::ios::out | std::ios::binary}; + + /// Seek direction: `beg`, `cur`, `end`. + using seekdir = std::ios::seekdir; + + /// Create new large object and open it. + /** + * @param t Backend transaction in which the object is to be created. + * @param mode Access mode, defaults to ios_base::in | ios_base::out | + * ios_base::binary. + */ + [[deprecated("Use blob instead.")]] explicit largeobjectaccess( + dbtransaction &t, openmode mode = default_mode); + + /// Open large object with given oid. + /** Convert combination of a transaction and object identifier into a + * large object identity. Does not affect the database. + * @param t Transaction in which the object is to be accessed. + * @param o Object identifier for the given object. + * @param mode Access mode, defaults to ios_base::in | ios_base::out | + * ios_base::binary. + */ + [[deprecated("Use blob instead.")]] largeobjectaccess( + dbtransaction &t, oid o, openmode mode = default_mode); + + /// Open given large object. + /** Open a large object with the given identity for reading and/or writing. + * @param t Transaction in which the object is to be accessed. + * @param o Identity for the large object to be accessed. + * @param mode Access mode, defaults to ios_base::in | ios_base::out | + * ios_base::binary. + */ + [[deprecated("Use blob instead.")]] largeobjectaccess( + dbtransaction &t, largeobject o, openmode mode = default_mode); + + /// Import large object from a local file and open it. + /** Creates a large object containing the data found in the given file. + * @param t Backend transaction in which the large object is to be created. + * @param file A filename on the client program's filesystem. + * @param mode Access mode, defaults to ios_base::in | ios_base::out. + */ + [[deprecated("Use blob instead.")]] largeobjectaccess( + dbtransaction &t, std::string_view file, openmode mode = default_mode); + + ~largeobjectaccess() noexcept { close(); } + + /// Object identifier. + /** The number returned by this function uniquely identifies the large object + * in the context of the database we're connected to. + */ + using largeobject::id; + + /// Export large object's contents to a local file. + /** Writes the data stored in the large object to the given file. + * @param file A filename on the client's filesystem. + */ + void to_file(std::string_view file) const + { + largeobject::to_file(m_trans, file); + } + + using largeobject::to_file; + + /** + * @name High-level access to object contents. + */ + //@{ + /// Write data to large object. + /** @warning The size of a write is currently limited to 2GB. + * + * @param buf Data to write. + * @param len Number of bytes from Buf to write. + */ + void write(char const buf[], std::size_t len); + + /// Write string to large object. + /** If not all bytes could be written, an exception is thrown. + * @param buf Data to write; no terminating zero is written. + */ + void write(std::string_view buf) { write(std::data(buf), std::size(buf)); } + + /// Read data from large object. + /** Throws an exception if an error occurs while reading. + * @param buf Location to store the read data in. + * @param len Number of bytes to try and read. + * @return Number of bytes read, which may be less than the number requested + * if the end of the large object is reached. + */ + size_type read(char buf[], std::size_t len); + + /// Seek in large object's data stream. + /** Throws an exception if an error occurs. + * @return The new position in the large object + */ + size_type seek(size_type dest, seekdir dir); + + /// Report current position in large object's data stream. + /** Throws an exception if an error occurs. + * @return The current position in the large object. + */ + [[nodiscard]] size_type tell() const; + //@} + + /** + * @name Low-level access to object contents. + * + * These functions provide a more "C-like" access interface, returning + * special values instead of throwing exceptions on error. These functions + * are generally best avoided in favour of the high-level access functions, + * which behave more like C++ functions should. + * + * Due to libpq's underlying API, some operations are limited to "int" + * sizes, typically 2 GB, even though a large object can grow much larger. + */ + //@{ + /// Seek in large object's data stream. + /** Does not throw exception in case of error; inspect return value and + * `errno` instead. + * @param dest Offset to go to. + * @param dir Origin to which dest is relative: ios_base::beg (from beginning + * of the object), ios_base::cur (from current access position), or + * ios_base;:end (from end of object). + * @return New position in large object, or -1 if an error occurred. + */ + pos_type cseek(off_type dest, seekdir dir) noexcept; + + /// Write to large object's data stream. + /** Does not throw exception in case of error; inspect return value and + * `errno` instead. + * @param buf Data to write. + * @param len Number of bytes to write. + * @return Number of bytes actually written, or -1 if an error occurred. + */ + off_type cwrite(char const buf[], std::size_t len) noexcept; + + /// Read from large object's data stream. + /** Does not throw exception in case of error; inspect return value and + * `errno` instead. + * @param buf Area where incoming bytes should be stored. + * @param len Number of bytes to read. + * @return Number of bytes actually read, or -1 if an error occurred.. + */ + off_type cread(char buf[], std::size_t len) noexcept; + + /// Report current position in large object's data stream. + /** Does not throw exception in case of error; inspect return value and + * `errno` instead. + * @return Current position in large object, of -1 if an error occurred. + */ + [[nodiscard]] pos_type ctell() const noexcept; + //@} + + /** + * @name Error/warning output + */ + //@{ + /// Issue message to transaction's notice processor. + void process_notice(zview) noexcept; + //@} + + using largeobject::remove; + + using largeobject::operator==; + using largeobject::operator!=; + using largeobject::operator<; + using largeobject::operator<=; + using largeobject::operator>; + using largeobject::operator>=; + + largeobjectaccess() = delete; + largeobjectaccess(largeobjectaccess const &) = delete; + largeobjectaccess operator=(largeobjectaccess const &) = delete; + +private: + PQXX_PRIVATE std::string reason(int err) const; + internal::pq::PGconn *raw_connection() const + { + return largeobject::raw_connection(m_trans); + } + + PQXX_PRIVATE void open(openmode mode); + void close() noexcept; + + dbtransaction &m_trans; + int m_fd = -1; +}; + + +/// Streambuf to use large objects in standard I/O streams. +/** @deprecated Access large objects directly using the @ref blob class. + * + * The standard streambuf classes provide uniform access to data storage such + * as files or string buffers, so they can be accessed using standard input or + * output streams. This streambuf implementation provided similar access to + * large objects, so they could be read and written using the same stream + * classes. + * + * This functionality was considered too fragile and complex, so it has been + * replaced with a single, much simpler class. + */ +template> +class largeobject_streambuf : public std::basic_streambuf +{ + using size_type = largeobject::size_type; + +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + using openmode = largeobjectaccess::openmode; + using seekdir = largeobjectaccess::seekdir; + + /// Default open mode: in, out, binary. + static constexpr auto default_mode{ + std::ios::in | std::ios::out | std::ios::binary}; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + [[deprecated("Use blob instead.")]] largeobject_streambuf( + dbtransaction &t, largeobject o, openmode mode = default_mode, + size_type buf_size = 512) : + m_bufsize{buf_size}, m_obj{t, o, mode}, m_g{nullptr}, m_p{nullptr} + { + initialize(mode); + } +#include "pqxx/internal/ignore-deprecated-post.hxx" + + [[deprecated("Use blob instead.")]] largeobject_streambuf( + dbtransaction &t, oid o, openmode mode = default_mode, + size_type buf_size = 512) : + m_bufsize{buf_size}, m_obj{t, o, mode}, m_g{nullptr}, m_p{nullptr} + { + initialize(mode); + } + + virtual ~largeobject_streambuf() noexcept + { + delete[] m_p; + delete[] m_g; + } + + /// For use by large object stream classes. + void process_notice(zview const &s) { m_obj.process_notice(s); } + +protected: + virtual int sync() override + { + // setg() sets eback, gptr, egptr. + this->setg(this->eback(), this->eback(), this->egptr()); + return overflow(eof()); + } + + virtual pos_type seekoff(off_type offset, seekdir dir, openmode) override + { + return adjust_eof(m_obj.cseek(largeobjectaccess::off_type(offset), dir)); + } + + virtual pos_type seekpos(pos_type pos, openmode) override + { + largeobjectaccess::pos_type const newpos{ + m_obj.cseek(largeobjectaccess::off_type(pos), std::ios::beg)}; + return adjust_eof(newpos); + } + + virtual int_type overflow(int_type ch) override + { + auto *const pp{this->pptr()}; + if (pp == nullptr) + return eof(); + auto *const pb{this->pbase()}; + int_type res{0}; + + if (pp > pb) + { + auto const write_sz{pp - pb}; + auto const written_sz{ + m_obj.cwrite(pb, static_cast(pp - pb))}; + if (internal::cmp_less_equal(written_sz, 0)) + throw internal_error{ + "pqxx::largeobject: write failed " + "(is transaction still valid on write or flush?), " + "libpq reports error"}; + else if (write_sz != written_sz) + throw internal_error{ + "pqxx::largeobject: write failed " + "(is transaction still valid on write or flush?), " + + std::to_string(written_sz) + "/" + std::to_string(write_sz) + + " bytes written"}; + auto const out{adjust_eof(written_sz)}; + + if constexpr (std::is_arithmetic_v) + res = check_cast(out, "largeobject position"sv); + else + res = int_type(out); + } + this->setp(m_p, m_p + m_bufsize); + + // Write that one more character, if it's there. + if (ch != eof()) + { + *this->pptr() = static_cast(ch); + this->pbump(1); + } + return res; + } + + virtual int_type overflow() { return overflow(eof()); } + + virtual int_type underflow() override + { + if (this->gptr() == nullptr) + return eof(); + auto *const eb{this->eback()}; + auto const res{adjust_eof( + m_obj.cread(this->eback(), static_cast(m_bufsize)))}; + this->setg( + eb, eb, eb + (res == eof() ? 0 : static_cast(res))); + return (res == eof() or res == 0) ? eof() : traits_type::to_int_type(*eb); + } + +private: + /// Shortcut for traits_type::eof(). + static int_type eof() { return traits_type::eof(); } + + /// Helper: change error position of -1 to EOF (probably a no-op). + template static std::streampos adjust_eof(INTYPE pos) + { + bool const at_eof{pos == -1}; + if constexpr (std::is_arithmetic_v) + { + return check_cast( + (at_eof ? eof() : pos), "large object seek"sv); + } + else + { + return std::streampos(at_eof ? eof() : pos); + } + } + + void initialize(openmode mode) + { + if ((mode & std::ios::in) != 0) + { + m_g = new char_type[unsigned(m_bufsize)]; + this->setg(m_g, m_g, m_g); + } + if ((mode & std::ios::out) != 0) + { + m_p = new char_type[unsigned(m_bufsize)]; + this->setp(m_p, m_p + m_bufsize); + } + } + + size_type const m_bufsize; + largeobjectaccess m_obj; + + /// Get & put buffers. + char_type *m_g, *m_p; +}; + + +/// Input stream that gets its data from a large object. +/** @deprecated Access large objects directly using the @ref blob class. + * + * This class worked like any other istream, but to read data from a large + * object. It supported all formatting and streaming operations of + * `std::istream`. + * + * This functionality was considered too fragile and complex, so it has been + * replaced with a single, much simpler class. + */ +template> +class basic_ilostream : public std::basic_istream +{ + using super = std::basic_istream; + +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + /// Create a basic_ilostream. + /** + * @param t Transaction in which this stream is to exist. + * @param o Large object to access. + * @param buf_size Size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_ilostream( + dbtransaction &t, largeobject o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{t, o, std::ios::in | std::ios::binary, buf_size} + { + super::init(&m_buf); + } +#include "pqxx/internal/ignore-deprecated-post.hxx" + + /// Create a basic_ilostream. + /** + * @param t Transaction in which this stream is to exist. + * @param o Identifier of a large object to access. + * @param buf_size Size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_ilostream( + dbtransaction &t, oid o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{t, o, std::ios::in | std::ios::binary, buf_size} + { + super::init(&m_buf); + } + +private: + largeobject_streambuf m_buf; +}; + +using ilostream = basic_ilostream; + + +/// Output stream that writes data back to a large object. +/** @deprecated Access large objects directly using the @ref blob class. + * + * This worked like any other ostream, but to write data to a large object. + * It supported all formatting and streaming operations of `std::ostream`. + * + * This functionality was considered too fragile and complex, so it has been + * replaced with a single, much simpler class. + */ +template> +class basic_olostream : public std::basic_ostream +{ + using super = std::basic_ostream; + +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + /// Create a basic_olostream. + /** + * @param t transaction in which this stream is to exist. + * @param o a large object to access. + * @param buf_size size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_olostream( + dbtransaction &t, largeobject o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{t, o, std::ios::out | std::ios::binary, buf_size} + { + super::init(&m_buf); + } +#include "pqxx/internal/ignore-deprecated-post.hxx" + + /// Create a basic_olostream. + /** + * @param t transaction in which this stream is to exist. + * @param o a large object to access. + * @param buf_size size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_olostream( + dbtransaction &t, oid o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{t, o, std::ios::out | std::ios::binary, buf_size} + { + super::init(&m_buf); + } + + ~basic_olostream() + { + try + { + m_buf.pubsync(); + m_buf.pubsync(); + } + catch (std::exception const &e) + { + m_buf.process_notice(e.what()); + } + } + +private: + largeobject_streambuf m_buf; +}; + +using olostream = basic_olostream; + + +/// Stream that reads and writes a large object. +/** @deprecated Access large objects directly using the @ref blob class. + * + * This worked like a std::iostream, but to read data from, or write data to, a + * large object. It supported all formatting and streaming operations of + * `std::iostream`. + * + * This functionality was considered too fragile and complex, so it has been + * replaced with a single, much simpler class. + */ +template> +class basic_lostream : public std::basic_iostream +{ + using super = std::basic_iostream; + +public: + using char_type = CHAR; + using traits_type = TRAITS; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + + /// Create a basic_lostream. + /** + * @param t Transaction in which this stream is to exist. + * @param o Large object to access. + * @param buf_size Size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_lostream( + dbtransaction &t, largeobject o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{ + t, o, std::ios::in | std::ios::out | std::ios::binary, buf_size} + { + super::init(&m_buf); + } + + /// Create a basic_lostream. + /** + * @param t Transaction in which this stream is to exist. + * @param o Large object to access. + * @param buf_size Size of buffer to use internally (optional). + */ + [[deprecated("Use blob instead.")]] basic_lostream( + dbtransaction &t, oid o, largeobject::size_type buf_size = 512) : + super{nullptr}, + m_buf{ + t, o, std::ios::in | std::ios::out | std::ios::binary, buf_size} + { + super::init(&m_buf); + } + + ~basic_lostream() + { + try + { + m_buf.pubsync(); + m_buf.pubsync(); + } + catch (std::exception const &e) + { + m_buf.process_notice(e.what()); + } + } + +private: + largeobject_streambuf m_buf; +}; + +using lostream = basic_lostream; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/nontransaction b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/nontransaction new file mode 100644 index 000000000..bb5b79724 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/nontransaction @@ -0,0 +1,8 @@ +/** pqxx::nontransaction class. + * + * pqxx::nontransaction provides nontransactional database access. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/nontransaction.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/nontransaction.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/nontransaction.hxx new file mode 100644 index 000000000..c50715594 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/nontransaction.hxx @@ -0,0 +1,76 @@ +/* Definition of the pqxx::nontransaction class. + * + * pqxx::nontransaction provides nontransactional database access + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/nontransaction instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_NONTRANSACTION +#define PQXX_H_NONTRANSACTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/connection.hxx" +#include "pqxx/result.hxx" +#include "pqxx/transaction.hxx" + +namespace pqxx +{ +using namespace std::literals; + +/// Simple "transaction" class offering no transactional integrity. +/** + * @ingroup transactions + * + * nontransaction, like transaction or any other transaction_base-derived + * class, provides access to a database through a connection. Unlike its + * siblings, however, nontransaction does not maintain any kind of + * transactional integrity. This may be useful eg. for read-only access to the + * database that does not require a consistent, atomic view on its data; or for + * operations that are not allowed within a backend transaction, such as + * creating tables. + * + * For queries that update the database, however, a real transaction is likely + * to be faster unless the transaction consists of only a single record update. + * + * Also, you can keep a nontransaction open for as long as you like. Actual + * back-end transactions are limited in lifespan, and will sometimes fail just + * because they took too long to execute or were left idle for too long. This + * will not happen with a nontransaction (although the connection may still + * time out, e.g. when the network is unavailable for a very long time). + * + * Any query executed in a nontransaction is committed immediately, and neither + * commit() nor abort() has any effect. + * + * Database features that require a backend transaction, such as cursors or + * large objects, will not work in a nontransaction. + */ +class PQXX_LIBEXPORT nontransaction final : public transaction_base +{ +public: + /// Constructor. + /** Create a "dummy" transaction. + * @param c Connection in which this "transaction" will operate. + * @param tname Optional tname for the transaction, beginning with a letter + * and containing only letters and digits. + */ + nontransaction(connection &c, std::string_view tname = ""sv) : + transaction_base{c, tname, std::shared_ptr{}} + { + register_transaction(); + } + + virtual ~nontransaction() override { close(); } + +private: + virtual void do_commit() override {} +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/notification b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/notification new file mode 100644 index 000000000..a0bd1c73e --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/notification @@ -0,0 +1,8 @@ +/** pqxx::notification_receiver functor interface. + * + * pqxx::notification_receiver handles incoming notifications. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/notification.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/notification.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/notification.hxx new file mode 100644 index 000000000..b59b8567a --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/notification.hxx @@ -0,0 +1,94 @@ +/* Definition of the pqxx::notification_receiver functor interface. + * + * pqxx::notification_receiver handles incoming notifications. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/notification instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_NOTIFICATION +#define PQXX_H_NOTIFICATION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#include "pqxx/types.hxx" + + +namespace pqxx +{ +/// "Observer" base class for notifications. +/** @addtogroup notification Notifications and Receivers + * + * To listen on a notification issued using the NOTIFY command, derive your own + * class from notification_receiver and define its function-call operator to + * perform whatever action you wish to take when the given notification + * arrives. Then create an object of that class and pass it to your connection. + * DO NOT use raw SQL to listen for notifications, or your attempts to listen + * won't be resumed when a connection fails--and you'll have no way to notice. + * + * Notifications never arrive inside a transaction, not even in a + * nontransaction. Therefore, you are free to open a transaction of your own + * inside your receiver's function invocation operator. + * + * Notifications you are listening for may arrive anywhere within libpqxx code, + * but be aware that **PostgreSQL defers notifications occurring inside + * transactions.** (This was done for excellent reasons; just think about what + * happens if the transaction where you happen to handle an incoming + * notification is later rolled back for other reasons). So if you're keeping + * a transaction open, don't expect any of your receivers on the same + * connection to be notified. + * + * (For very similar reasons, outgoing notifications are also not sent until + * the transaction that sends them commits.) + * + * Multiple receivers on the same connection may listen on a notification of + * the same name. An incoming notification is processed by invoking all + * receivers (zero or more) of the same name. + */ +class PQXX_LIBEXPORT PQXX_NOVTABLE notification_receiver +{ +public: + /// Register the receiver with a connection. + /** + * @param c Connnection to operate on. + * @param channel Name of the notification to listen for. + */ + notification_receiver(connection &c, std::string_view channel); + /// Register the receiver with a connection. + notification_receiver(notification_receiver const &) = delete; + /// Register the receiver with a connection. + notification_receiver &operator=(notification_receiver const &) = delete; + /// Deregister the receiver. + virtual ~notification_receiver(); + + /// The channel that this receiver listens on. + [[nodiscard]] std::string const &channel() const & { return m_channel; } + + // TODO: Change API to take payload as zview instead of string ref. + /// Overridable: action to invoke when notification arrives. + /** + * @param payload An optional string that may have been passed to the NOTIFY + * command. + * @param backend_pid Process ID of the database backend process that served + * our connection when the notification arrived. The actual process ID + * behind the connection may have changed by the time this method is called. + */ + virtual void operator()(std::string const &payload, int backend_pid) = 0; + +protected: + connection &conn() const noexcept { return m_conn; } + +private: + connection &m_conn; + std::string m_channel; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/params b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/params new file mode 100644 index 000000000..4098782aa --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/params @@ -0,0 +1,8 @@ +/** Helper classes for passing statement parameters. + * + * Use these for prepared statements and parameterised statements. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/params.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/params.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/params.hxx new file mode 100644 index 000000000..2d29cdfed --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/params.hxx @@ -0,0 +1,383 @@ +/* Helpers for prepared statements and parameterised statements. + * + * See the connection class for more about such statements. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_PARAMS +#define PQXX_H_PARAMS + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/statement_parameters.hxx" +#include "pqxx/types.hxx" + + +/// @deprecated The new @ref params class replaces all of this. +namespace pqxx::prepare +{ +/// Pass a number of statement parameters only known at runtime. +/** @deprecated Use @ref params instead. + * + * When you call any of the `exec_params` functions, the number of arguments + * is normally known at compile time. This helper function supports the case + * where it is not. + * + * Use this function to pass a variable number of parameters, based on a + * sequence ranging from `begin` to `end` exclusively. + * + * The technique combines with the regular static parameters. You can use it + * to insert dynamic parameter lists in any place, or places, among the call's + * parameters. You can even insert multiple dynamic sequences. + * + * @param begin A pointer or iterator for iterating parameters. + * @param end A pointer or iterator for iterating parameters. + * @return An object representing the parameters. + */ +template +[[deprecated("Use the params class instead.")]] constexpr inline auto +make_dynamic_params(IT begin, IT end) +{ + return pqxx::internal::dynamic_params(begin, end); +} + + +/// Pass a number of statement parameters only known at runtime. +/** @deprecated Use @ref params instead. + * + * When you call any of the `exec_params` functions, the number of arguments + * is normally known at compile time. This helper function supports the case + * where it is not. + * + * Use this function to pass a variable number of parameters, based on a + * container of parameter values. + * + * The technique combines with the regular static parameters. You can use it + * to insert dynamic parameter lists in any place, or places, among the call's + * parameters. You can even insert multiple dynamic containers. + * + * @param container A container of parameter values. + * @return An object representing the parameters. + */ +template +[[deprecated("Use the params class instead.")]] constexpr inline auto +make_dynamic_params(C const &container) +{ + using IT = typename C::const_iterator; +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return pqxx::internal::dynamic_params{container}; +#include "pqxx/internal/ignore-deprecated-post.hxx" +} + + +/// Pass a number of statement parameters only known at runtime. +/** @deprecated Use @ref params instead. + * + * When you call any of the `exec_params` functions, the number of arguments + * is normally known at compile time. This helper function supports the case + * where it is not. + * + * Use this function to pass a variable number of parameters, based on a + * container of parameter values. + * + * The technique combines with the regular static parameters. You can use it + * to insert dynamic parameter lists in any place, or places, among the call's + * parameters. You can even insert multiple dynamic containers. + * + * @param container A container of parameter values. + * @param accessor For each parameter `p`, pass `accessor(p)`. + * @return An object representing the parameters. + */ +template +[[deprecated("Use the params class instead.")]] constexpr inline auto +make_dynamic_params(C &container, ACCESSOR accessor) +{ + using IT = decltype(std::begin(container)); +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return pqxx::internal::dynamic_params{container, accessor}; +#include "pqxx/internal/ignore-deprecated-post.hxx" +} +} // namespace pqxx::prepare + + +namespace pqxx +{ +/// Generate parameter placeholders for use in an SQL statement. +/** When you want to pass parameters to a prepared statement or a parameterised + * statement, you insert placeholders into the SQL. During invocation, the + * database replaces those with the respective parameter values you passed. + * + * The placeholders look like `$1` (for the first parameter value), `$2` (for + * the second), and so on. You can just write those directly in your + * statement. But for those rare cases where it becomes difficult to track + * which number a placeholder should have, you can use a `placeholders` object + * to count and generate them in order. + */ +template class placeholders +{ +public: + /// Maximum number of parameters we support. + static inline constexpr unsigned int max_params{ + (std::numeric_limits::max)()}; + + placeholders() + { + static constexpr auto initial{"$1\0"sv}; + initial.copy(std::data(m_buf), std::size(initial)); + } + + /// Read an ephemeral version of the current placeholder text. + /** @warning Changing the current placeholder number will overwrite this. + * Use the view immediately, or lose it. + */ + constexpr zview view() const &noexcept + { + return zview{std::data(m_buf), m_len}; + } + + /// Read the current placeholder text, as a `std::string`. + /** This will be slightly slower than converting to a `zview`. With most + * C++ implementations however, until you get into ridiculous numbers of + * parameters, the string will benefit from the Short String Optimization, or + * SSO. + */ + std::string get() const { return std::string(std::data(m_buf), m_len); } + + /// Move on to the next parameter. + void next() & + { + if (m_current >= max_params) + throw range_error{pqxx::internal::concat( + "Too many parameters in one statement: limit is ", max_params, ".")}; + ++m_current; + if (m_current % 10 == 0) + { + // Carry the 1. Don't get too clever for this relatively rare + // case, just rewrite the entire number. Leave the $ in place + // though. + char *const data{std::data(m_buf)}; + char *const end{string_traits::into_buf( + data + 1, data + std::size(m_buf), m_current)}; + // (Subtract because we don't include the trailing zero.) + m_len = check_cast(end - data, "placeholders counter") - 1; + } + else + { + PQXX_LIKELY + // Shortcut for the common case: just increment that last digit. + ++m_buf[m_len - 1]; + } + } + + /// Return the current placeholder number. The initial placeholder is 1. + COUNTER count() const noexcept { return m_current; } + +private: + /// Current placeholder number. Starts at 1. + COUNTER m_current = 1; + + /// Length of the current placeholder string, not including trailing zero. + COUNTER m_len = 2; + + /// Text buffer where we render the placeholders, with a trailing zero. + /** We keep reusing this for every subsequent placeholder, just because we + * don't like string allocations. + * + * Maximum length is the maximum base-10 digits that COUNTER can fully + * represent, plus 1 more for the extra digit that it can only partially + * fill up, plus room for the dollar sign and the trailing zero. + */ + std::array::digits10 + 3> m_buf; +}; + + +/// Build a parameter list for a parameterised or prepared statement. +/** When calling a parameterised statement or a prepared statement, you can + * pass parameters into the statement directly in the invocation, as + * additional arguments to `exec_prepared` or `exec_params`. But in + * complex cases, sometimes that's just not convenient. + * + * In those situations, you can create a `params` and append your parameters + * into that, one by one. Then you pass the `params` to `exec_prepared` or + * `exec_params`. + * + * Combinations also work: if you have a `params` containing a string + * parameter, and you call `exec_params` with an `int` argument followed by + * your `params`, you'll be passing the `int` as the first parameter and + * the string as the second. You can even insert a `params` in a `params`, + * or pass two `params` objects to a statement. + */ +class PQXX_LIBEXPORT params +{ +public: + params() = default; + + /// Pre-populate a `params` with `args`. Feel free to add more later. + template constexpr params(Args &&...args) + { + reserve(sizeof...(args)); + append_pack(std::forward(args)...); + } + + /// Pre-allocate room for at least `n` parameters. + /** This is not needed, but it may improve efficiency. + * + * Reserve space if you're going to add parameters individually, and you've + * got some idea of how many there are going to be. It may save some + * memory re-allocations. + */ + void reserve(std::size_t n) &; + + // C++20: constexpr. + /// Get the number of parameters currently in this `params`. + [[nodiscard]] auto size() const noexcept { return m_params.size(); } + + // C++20: Use the vector's ssize() directly and go noexcept+constexpr. + /// Get the number of parameters (signed). + /** Unlike `size()`, this is not yet `noexcept`. That's because C++17's + * `std::vector` does not have a `ssize()` member function. These member + * functions are `noexcept`, but `std::size()` and `std::ssize()` are + * not. + */ + [[nodiscard]] auto ssize() const { return pqxx::internal::ssize(m_params); } + + /// Append a null value. + void append() &; + + /// Append a non-null zview parameter. + /** The underlying data must stay valid for as long as the `params` + * remains active. + */ + void append(zview) &; + + /// Append a non-null string parameter. + /** Copies the underlying data into internal storage. For best efficiency, + * use the @ref zview variant if you can, or `std::move()` + */ + void append(std::string const &) &; + + /// Append a non-null string parameter. + void append(std::string &&) &; + + /// Append a non-null binary parameter. + /** The underlying data must stay valid for as long as the `params` + * remains active. + */ + void append(std::basic_string_view) &; + + /// Append a non-null binary parameter. + /** Copies the underlying data into internal storage. For best efficiency, + * use the `std::basic_string_view` variant if you can, or + * `std::move()`. + */ + void append(std::basic_string const &) &; + +#if defined(PQXX_HAVE_CONCEPTS) + /// Append a non-null binary parameter. + /** The `data` object must stay in place and unchanged, for as long as the + * `params` remains active. + */ + template void append(DATA const &data) & + { + append( + std::basic_string_view{std::data(data), std::size(data)}); + } +#endif // PQXX_HAVE_CONCEPTS + + /// Append a non-null binary parameter. + void append(std::basic_string &&) &; + + /// @deprecated Append binarystring parameter. + /** The binarystring must stay valid for as long as the `params` remains + * active. + */ + void append(binarystring const &value) &; + + /// Append all parameters from value. + template + void append(pqxx::internal::dynamic_params const &value) & + { + for (auto ¶m : value) append(value.access(param)); + } + + void append(params const &value) &; + + void append(params &&value) &; + + /// Append a non-null parameter, converting it to its string + /// representation. + template void append(TYPE const &value) & + { + // TODO: Pool storage for multiple string conversions in one buffer? + if constexpr (nullness>::always_null) + { + ignore_unused(value); + m_params.emplace_back(); + } + else if (is_null(value)) + { + m_params.emplace_back(); + } + else + { + m_params.emplace_back(entry{to_string(value)}); + } + } + + /// Append all elements of `range` as parameters. + template void append_multi(RANGE const &range) & + { +#if defined(PQXX_HAVE_CONCEPTS) + if constexpr (std::ranges::sized_range) + reserve(std::size(*this) + std::size(range)); +#endif + for (auto &value : range) append(value); + } + + /// For internal use: Generate a `params` object for use in calls. + /** The params object encapsulates the pointers which we will need to pass + * to libpq when calling a parameterised or prepared statement. + * + * The pointers in the params will refer to storage owned by either the + * params object, or the caller. This is not a problem because a + * `c_params` object is guaranteed to live only while the call is going on. + * As soon as we climb back out of that call tree, we're done with that + * data. + */ + pqxx::internal::c_params make_c_params() const; + +private: + /// Recursively append a pack of params. + template + void append_pack(Arg &&arg, More &&...args) + { + this->append(std::forward(arg)); + // Recurse for remaining args. + append_pack(std::forward(args)...); + } + + /// Terminating case: append an empty parameter pack. It's not hard BTW. + constexpr void append_pack() noexcept {} + + // The way we store a parameter depends on whether it's binary or text + // (most types are text), and whether we're responsible for storing the + // contents. + using entry = std::variant< + std::nullptr_t, zview, std::string, std::basic_string_view, + std::basic_string>; + std::vector m_params; + + static constexpr std::string_view s_overflow{ + "Statement parameter length overflow."sv}; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pipeline b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pipeline new file mode 100644 index 000000000..bf828843a --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pipeline @@ -0,0 +1,8 @@ +/** pqxx::pipeline class. + * + * Throughput-optimized query interface. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/pipeline.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pipeline.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pipeline.hxx new file mode 100644 index 000000000..049dcdd58 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pipeline.hxx @@ -0,0 +1,237 @@ +/* Definition of the pqxx::pipeline class. + * + * Throughput-optimized mechanism for executing queries. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/pipeline instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_PIPELINE +#define PQXX_H_PIPELINE + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include + +#include "pqxx/transaction_base.hxx" + + +namespace pqxx +{ +// TODO: libpq 14 introduced a similar "pipeline mode." Can we use that? + +/// Processes several queries in FIFO manner, optimized for high throughput. +/** Use a pipeline if you want to keep doing useful work while your queries are + * executing. Result retrieval is decoupled from execution request; queries + * "go in at the front" and results "come out the back." + * + * Actually, you can retrieve the results in any order if you want, but it may + * lead to surprising "time travel" effects if any of the queries fails. In + * particular, syntax errors in the queries can confuse things and show up too + * early in the stream of results. + * + * Generally, if any of the queries fails, it will throw an exception at the + * point where you request its result. But it may happen earlier, especially + * if you request results out of chronological order. + * + * @warning While a pipeline is active, you cannot execute queries, open + * streams, etc. on the same transaction. A transaction can have at most one + * object of a type derived from @ref pqxx::transaction_focus active on it at a + * time. + */ +class PQXX_LIBEXPORT pipeline : public transaction_focus +{ +public: + /// Identifying numbers for queries. + using query_id = long; + + pipeline(pipeline const &) = delete; + pipeline &operator=(pipeline const &) = delete; + + /// Start a pipeline. + explicit pipeline(transaction_base &t) : transaction_focus{t, s_classname} + { + init(); + } + /// Start a pipeline. Assign it a name, for more helpful error messages. + pipeline(transaction_base &t, std::string_view tname) : + transaction_focus{t, s_classname, tname} + { + init(); + } + + /// Close the pipeline. + ~pipeline() noexcept; + + /// Add query to the pipeline. + /** Queries accumulate in the pipeline, which sends them to the backend in a + * batch separated by semicolons. The queries you insert must not use this + * trick themselves, or the pipeline will get hopelessly confused! + * + * @return Identifier for this query, unique only within this pipeline. + */ + query_id insert(std::string_view) &; + + /// Wait for all ongoing or pending operations to complete, and detach. + /** Detaches from the transaction when done. + * + * This does not produce the queries' results, so it may not report any + * errors which may have occurred in their execution. To be sure that your + * statements succeeded, call @ref retrieve until the pipeline is empty. + */ + void complete(); + + /// Forget all ongoing or pending operations and retrieved results. + /** Queries already sent to the backend may still be completed, depending + * on implementation and timing. + * + * Any error state (unless caused by an internal error) will also be cleared. + * This is mostly useful in a nontransaction, since a backend transaction is + * aborted automatically when an error occurs. + * + * Detaches from the transaction when done. + */ + void flush(); + + /// Cancel ongoing query, if any. + /** May cancel any or all of the queries that have been inserted at this + * point whose results have not yet been retrieved. If the pipeline lives in + * a backend transaction, that transaction may be left in a nonfunctional + * state in which it can only be aborted. + * + * Therefore, either use this function in a nontransaction, or abort the + * transaction after calling it. + */ + void cancel(); + + /// Is result for given query available? + [[nodiscard]] bool is_finished(query_id) const; + + /// Retrieve result for given query. + /** If the query failed for whatever reason, this will throw an exception. + * The function will block if the query has not finished yet. + * @warning If results are retrieved out-of-order, i.e. in a different order + * than the one in which their queries were inserted, errors may "propagate" + * to subsequent queries. + */ + result retrieve(query_id qid) + { + return retrieve(m_queries.find(qid)).second; + } + + /// Retrieve oldest unretrieved result (possibly wait for one). + /** @return The query's identifier and its result set. */ + std::pair retrieve(); + + [[nodiscard]] bool empty() const noexcept { return std::empty(m_queries); } + + /// Set maximum number of queries to retain before issuing them to the + /// backend. + /** The pipeline will perform better if multiple queries are issued at once, + * but retaining queries until the results are needed (as opposed to issuing + * them to the backend immediately) may negate any performance benefits the + * pipeline can offer. + * + * Recommended practice is to set this value no higher than the number of + * queries you intend to insert at a time. + * @param retain_max A nonnegative "retention capacity;" passing zero will + * cause queries to be issued immediately + * @return Old retention capacity + */ + int retain(int retain_max = 2) &; + + + /// Resume retained query emission. Harmless when not needed. + void resume() &; + +private: + struct PQXX_PRIVATE Query + { + explicit Query(std::string_view q) : + query{std::make_shared(q)} + {} + + std::shared_ptr query; + result res; + }; + + using QueryMap = std::map; + + void init(); + void attach(); + void detach(); + + /// Upper bound to query id's. + static constexpr query_id qid_limit() noexcept + { + // Parenthesise this to work around an eternal Visual C++ problem: + // Without the extra parentheses, unless NOMINMAX is defined, the + // preprocessor will mistake this "max" for its annoying built-in macro + // of the same name. + return (std::numeric_limits::max)(); + } + + /// Create new query_id. + PQXX_PRIVATE query_id generate_id(); + + bool have_pending() const noexcept + { + return m_issuedrange.second != m_issuedrange.first; + } + + PQXX_PRIVATE void issue(); + + /// The given query failed; never issue anything beyond that. + void set_error_at(query_id qid) noexcept + { + PQXX_UNLIKELY + if (qid < m_error) + m_error = qid; + } + + /// Throw pqxx::internal_error. + [[noreturn]] PQXX_PRIVATE void internal_error(std::string const &err); + + PQXX_PRIVATE bool obtain_result(bool expect_none = false); + + PQXX_PRIVATE void obtain_dummy(); + PQXX_PRIVATE void get_further_available_results(); + PQXX_PRIVATE void check_end_results(); + + /// Receive any results that happen to be available; it's not urgent. + PQXX_PRIVATE void receive_if_available(); + + /// Receive results, up to stop if possible. + PQXX_PRIVATE void receive(pipeline::QueryMap::const_iterator stop); + std::pair retrieve(pipeline::QueryMap::iterator); + + QueryMap m_queries; + std::pair m_issuedrange; + int m_retain = 0; + int m_num_waiting = 0; + query_id m_q_id = 0; + + /// Is there a "dummy query" pending? + bool m_dummy_pending = false; + + /// Point at which an error occurred; no results beyond it will be available + query_id m_error = qid_limit(); + + /// Encoding. + /** We store this in the object to avoid the risk of exceptions at awkward + * moments. + */ + internal::encoding_group m_encoding; + + static constexpr std::string_view s_classname{"pipeline"}; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pqxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pqxx new file mode 100644 index 000000000..17a8eaa9c --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/pqxx @@ -0,0 +1,28 @@ +/// Convenience header: include all libpqxx definitions. +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/array.hxx" +#include "pqxx/binarystring.hxx" +#include "pqxx/blob.hxx" +#include "pqxx/connection.hxx" +#include "pqxx/cursor.hxx" +#include "pqxx/errorhandler.hxx" +#include "pqxx/except.hxx" +#include "pqxx/largeobject.hxx" +#include "pqxx/nontransaction.hxx" +#include "pqxx/notification.hxx" +#include "pqxx/params.hxx" +#include "pqxx/pipeline.hxx" +#include "pqxx/prepared_statement.hxx" +#include "pqxx/result.hxx" +#include "pqxx/internal/result_iterator.hxx" +#include "pqxx/internal/result_iter.hxx" +#include "pqxx/robusttransaction.hxx" +#include "pqxx/row.hxx" +#include "pqxx/stream_from.hxx" +#include "pqxx/stream_to.hxx" +#include "pqxx/subtransaction.hxx" +#include "pqxx/transaction.hxx" +#include "pqxx/transactor.hxx" + +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/prepared_statement b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/prepared_statement new file mode 100644 index 000000000..674be7090 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/prepared_statement @@ -0,0 +1,3 @@ +/// @deprecated Include @c instead. + +#include "params.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/prepared_statement.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/prepared_statement.hxx new file mode 100644 index 000000000..674be7090 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/prepared_statement.hxx @@ -0,0 +1,3 @@ +/// @deprecated Include @c instead. + +#include "params.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/range b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/range new file mode 100644 index 000000000..11985eca4 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/range @@ -0,0 +1,6 @@ +/** Client-side support for SQL range types. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/range.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/range.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/range.hxx new file mode 100644 index 000000000..dc480e4b7 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/range.hxx @@ -0,0 +1,515 @@ +#ifndef PQXX_H_RANGE +#define PQXX_H_RANGE + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include + +#include "pqxx/internal/array-composite.hxx" +#include "pqxx/internal/concat.hxx" + +namespace pqxx +{ +/// An _unlimited_ boundary value to a @ref pqxx::range. +/** Use this as a lower or upper bound for a range if the range should extend + * to infinity on that side. + * + * An unlimited boundary is always inclusive of "infinity" values, if the + * range's value type supports them. + */ +struct no_bound +{ + template constexpr bool extends_down_to(TYPE const &) const + { + return true; + } + template constexpr bool extends_up_to(TYPE const &) const + { + return true; + } +}; + + +/// An _inclusive_ boundary value to a @ref pqxx::range. +/** Use this as a lower or upper bound for a range if the range should include + * the value. + */ +template class inclusive_bound +{ +public: + inclusive_bound() = delete; + explicit inclusive_bound(TYPE const &value) : m_value{value} + { + if (is_null(value)) + throw argument_error{"Got null value as an inclusive range bound."}; + } + + [[nodiscard]] constexpr TYPE const &get() const &noexcept { return m_value; } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + /// Would this bound, as a lower bound, include value? + [[nodiscard]] bool extends_down_to(TYPE const &value) const + { + return not(value < m_value); + } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + /// Would this bound, as an upper bound, include value? + [[nodiscard]] bool extends_up_to(TYPE const &value) const + { + return not(m_value < value); + } + +private: + TYPE m_value; +}; + + +/// An _exclusive_ boundary value to a @ref pqxx::range. +/** Use this as a lower or upper bound for a range if the range should _not_ + * include the value. + */ +template class exclusive_bound +{ +public: + exclusive_bound() = delete; + explicit exclusive_bound(TYPE const &value) : m_value{value} + { + if (is_null(value)) + throw argument_error{"Got null value as an exclusive range bound."}; + } + + [[nodiscard]] constexpr TYPE const &get() const &noexcept { return m_value; } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + /// Would this bound, as a lower bound, include value? + [[nodiscard]] bool extends_down_to(TYPE const &value) const + { + return m_value < value; + } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + /// Would this bound, as an upper bound, include value? + [[nodiscard]] bool extends_up_to(TYPE const &value) const + { + return value < m_value; + } + +private: + TYPE m_value; +}; + + +/// A range boundary value. +/** A range bound is either no bound at all; or an inclusive bound; or an + * exclusive bound. Pass one of the three to the constructor. + */ +template class range_bound +{ +public: + range_bound() = delete; + // TODO: constexpr and/or noexcept if underlying constructor supports it. + range_bound(no_bound) : m_bound{} {} + // TODO: constexpr and/or noexcept if underlying constructor supports it. + range_bound(inclusive_bound const &bound) : m_bound{bound} {} + // TODO: constexpr and/or noexcept if underlying constructor supports it. + range_bound(exclusive_bound const &bound) : m_bound{bound} {} + // TODO: constexpr and/or noexcept if underlying constructor supports it. + range_bound(range_bound const &) = default; + // TODO: constexpr and/or noexcept if underlying constructor supports it. + range_bound(range_bound &&) = default; + + // TODO: constexpr and/or noexcept if underlying operators support it. + bool operator==(range_bound const &rhs) const + { + if (this->is_limited()) + return ( + rhs.is_limited() and (this->is_inclusive() == rhs.is_inclusive()) and + (*this->value() == *rhs.value())); + else + return not rhs.is_limited(); + } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + bool operator!=(range_bound const &rhs) const { return not(*this == rhs); } + range_bound &operator=(range_bound const &) = default; + range_bound &operator=(range_bound &&) = default; + + /// Is this a finite bound? + constexpr bool is_limited() const noexcept + { + return not std::holds_alternative(m_bound); + } + + /// Is this boundary an inclusive one? + constexpr bool is_inclusive() const noexcept + { + return std::holds_alternative>(m_bound); + } + + /// Is this boundary an exclusive one? + constexpr bool is_exclusive() const noexcept + { + return std::holds_alternative>(m_bound); + } + + // TODO: constexpr/noexcept if underlying function supports it. + /// Would this bound, as a lower bound, include `value`? + bool extends_down_to(TYPE const &value) const + { + return std::visit( + [&value](auto const &bound) { return bound.extends_down_to(value); }, + m_bound); + } + + // TODO: constexpr/noexcept if underlying function supports it. + /// Would this bound, as an upper bound, include `value`? + bool extends_up_to(TYPE const &value) const + { + return std::visit( + [&value](auto const &bound) { return bound.extends_up_to(value); }, + m_bound); + } + + /// Return bound value, or `nullptr` if it's not limited. + [[nodiscard]] constexpr TYPE const *value() const &noexcept + { + return std::visit( + [](auto const &bound) noexcept { + using bound_t = std::decay_t; + if constexpr (std::is_same_v) + return static_cast(nullptr); + else + return &bound.get(); + }, + m_bound); + } + +private: + std::variant, exclusive_bound> m_bound; +}; + + +// C++20: Concepts for comparisons, construction, etc. +/// A C++ equivalent to PostgreSQL's range types. +/** You can use this as a client-side representation of a "range" in SQL. + * + * PostgreSQL defines several range types, differing in the data type over + * which they range. You can also define your own range types. + * + * Usually you'll want the server to deal with ranges. But on occasions where + * you need to work with them client-side, you may want to use @ref + * pqxx::range. (In cases where all you do is pass them along to the server + * though, it's not worth the complexity. In that case you might as well treat + * ranges as just strings.) + * + * For documentation on PostgreSQL's range types, see: + * https://www.postgresql.org/docs/current/rangetypes.html + * + * The value type must be copyable and default-constructible, and support the + * less-than (`<`) and equals (`==`) comparisons. Value initialisation must + * produce a consistent value. + */ +template class range +{ +public: + /// Create a range. + /** For each of the two bounds, pass a @ref no_bound, @ref inclusive_bound, + * or + * @ref exclusive_bound. + */ + range(range_bound lower, range_bound upper) : + m_lower{lower}, m_upper{upper} + { + if ( + lower.is_limited() and upper.is_limited() and + (*upper.value() < *lower.value())) + throw range_error{internal::concat( + "Range's lower bound (", *lower.value(), + ") is greater than its upper bound (", *upper.value(), ").")}; + } + + // TODO: constexpr and/or noexcept if underlying constructor supports it. + /// Create an empty range. + /** SQL has a separate literal to denote an empty range, but any range which + * encompasses no values is an empty range. + */ + range() : + m_lower{exclusive_bound{TYPE{}}}, + m_upper{exclusive_bound{TYPE{}}} + {} + + // TODO: constexpr and/or noexcept if underlying operators support it. + bool operator==(range const &rhs) const + { + return (this->lower_bound() == rhs.lower_bound() and + this->upper_bound() == rhs.upper_bound()) or + (this->empty() and rhs.empty()); + } + + // TODO: constexpr and/or noexcept if underlying operator supports it. + bool operator!=(range const &rhs) const { return !(*this == rhs); } + + range(range const &) = default; + range(range &&) = default; + range &operator=(range const &) = default; + range &operator=(range &&) = default; + + // TODO: constexpr and/or noexcept if underlying operator supports it. + /// Is this range clearly empty? + /** An empty range encompasses no values. + * + * It is possible to "fool" this. For example, if your range is of an + * integer type and has exclusive bounds of 0 and 1, it encompasses no values + * but its `empty()` will return false. The PostgreSQL implementation, by + * contrast, will notice that it is empty. Similar things can happen for + * floating-point types, but with more subtleties and edge cases. + */ + bool empty() const + { + return (m_lower.is_exclusive() or m_upper.is_exclusive()) and + m_lower.is_limited() and m_upper.is_limited() and + not(*m_lower.value() < *m_upper.value()); + } + + // TODO: constexpr and/or noexcept if underlying functions support it. + /// Does this range encompass `value`? + bool contains(TYPE value) const + { + return m_lower.extends_down_to(value) and m_upper.extends_up_to(value); + } + + // TODO: constexpr and/or noexcept if underlying operators support it. + /// Does this range encompass all of `other`? + /** This function is not particularly smart. It does not know, for example, + * that integer ranges `[0,9]` and `[0,10)` contain the same values. + */ + bool contains(range const &other) const + { + return (*this & other) == other; + } + + [[nodiscard]] constexpr range_bound const & + lower_bound() const &noexcept + { + return m_lower; + } + [[nodiscard]] constexpr range_bound const & + upper_bound() const &noexcept + { + return m_upper; + } + + // TODO: constexpr and/or noexcept if underlying operators support it. + /// Intersection of two ranges. + /** Returns a range describing those values which are in both ranges. + */ + range operator&(range const &other) const + { + range_bound lower{no_bound{}}; + if (not this->lower_bound().is_limited()) + lower = other.lower_bound(); + else if (not other.lower_bound().is_limited()) + lower = this->lower_bound(); + else if (*this->lower_bound().value() < *other.lower_bound().value()) + lower = other.lower_bound(); + else if (*other.lower_bound().value() < *this->lower_bound().value()) + lower = this->lower_bound(); + else if (this->lower_bound().is_exclusive()) + lower = this->lower_bound(); + else + lower = other.lower_bound(); + + range_bound upper{no_bound{}}; + if (not this->upper_bound().is_limited()) + upper = other.upper_bound(); + else if (not other.upper_bound().is_limited()) + upper = this->upper_bound(); + else if (*other.upper_bound().value() < *this->upper_bound().value()) + upper = other.upper_bound(); + else if (*this->upper_bound().value() < *other.upper_bound().value()) + upper = this->upper_bound(); + else if (this->upper_bound().is_exclusive()) + upper = this->upper_bound(); + else + upper = other.upper_bound(); + + if ( + lower.is_limited() and upper.is_limited() and + (*upper.value() < *lower.value())) + return {}; + else + return {lower, upper}; + } + + /// Convert to another base type. + template operator range() const + { + range_bound lower{no_bound{}}, upper{no_bound{}}; + if (lower_bound().is_inclusive()) + lower = inclusive_bound{*lower_bound().value()}; + else if (lower_bound().is_exclusive()) + lower = exclusive_bound{*lower_bound().value()}; + + if (upper_bound().is_inclusive()) + upper = inclusive_bound{*upper_bound().value()}; + else if (upper_bound().is_exclusive()) + upper = exclusive_bound{*upper_bound().value()}; + + return {lower, upper}; + } + +private: + range_bound m_lower, m_upper; +}; + + +/// String conversions for a @ref range type. +/** Conversion assumes that either your client encoding is UTF-8, or the values + * are pure ASCII. + */ +template struct string_traits> +{ + [[nodiscard]] static inline zview + to_buf(char *begin, char *end, range const &value) + { + return generic_to_buf(begin, end, value); + } + + static inline char * + into_buf(char *begin, char *end, range const &value) + { + if (value.empty()) + { + if ((end - begin) <= internal::ssize(s_empty)) + throw conversion_overrun{s_overrun.c_str()}; + char *here = begin + s_empty.copy(begin, std::size(s_empty)); + *here++ = '\0'; + return here; + } + else + { + if (end - begin < 4) + throw conversion_overrun{s_overrun.c_str()}; + char *here = begin; + *here++ = + (static_cast(value.lower_bound().is_inclusive() ? '[' : '(')); + TYPE const *lower{value.lower_bound().value()}; + // Convert bound (but go back to overwrite that trailing zero). + if (lower != nullptr) + here = string_traits::into_buf(here, end, *lower) - 1; + *here++ = ','; + TYPE const *upper{value.upper_bound().value()}; + // Convert bound (but go back to overwrite that trailing zero). + if (upper != nullptr) + here = string_traits::into_buf(here, end, *upper) - 1; + if ((end - here) < 2) + throw conversion_overrun{s_overrun.c_str()}; + *here++ = + static_cast(value.upper_bound().is_inclusive() ? ']' : ')'); + *here++ = '\0'; + return here; + } + } + + [[nodiscard]] static inline range from_string(std::string_view text) + { + if (std::size(text) < 3) + throw pqxx::conversion_error{err_bad_input(text)}; + bool left_inc{false}; + switch (text[0]) + { + case '[': left_inc = true; break; + + case '(': break; + + case 'e': + case 'E': + if ( + (std::size(text) != std::size(s_empty)) or + (text[1] != 'm' and text[1] != 'M') or + (text[2] != 'p' and text[2] != 'P') or + (text[3] != 't' and text[3] != 'T') or + (text[4] != 'y' and text[4] != 'Y')) + throw pqxx::conversion_error{err_bad_input(text)}; + return {}; + break; + + default: throw pqxx::conversion_error{err_bad_input(text)}; + } + + auto scan{internal::get_glyph_scanner(internal::encoding_group::UTF8)}; + // The field parser uses this to track which field it's parsing, and + // when not to expect a field separator. + std::size_t index{0}; + // The last field we expect to see. + static constexpr std::size_t last{1}; + // Current parsing position. We skip the opening parenthesis or bracket. + std::size_t pos{1}; + // The string may leave out either bound to indicate that it's unlimited. + std::optional lower, upper; + // We reuse the same field parser we use for composite values and arrays. + internal::parse_composite_field(index, text, pos, lower, scan, last); + internal::parse_composite_field(index, text, pos, upper, scan, last); + + // We need one more character: the closing parenthesis or bracket. + if (pos != std::size(text)) + throw pqxx::conversion_error{err_bad_input(text)}; + char const closing{text[pos - 1]}; + if (closing != ')' and closing != ']') + throw pqxx::conversion_error{err_bad_input(text)}; + bool const right_inc{closing == ']'}; + + range_bound lower_bound{no_bound{}}, upper_bound{no_bound{}}; + if (lower) + { + if (left_inc) + lower_bound = inclusive_bound{*lower}; + else + lower_bound = exclusive_bound{*lower}; + } + if (upper) + { + if (right_inc) + upper_bound = inclusive_bound{*upper}; + else + upper_bound = exclusive_bound{*upper}; + } + + return {lower_bound, upper_bound}; + } + + [[nodiscard]] static inline constexpr std::size_t + size_buffer(range const &value) noexcept + { + TYPE const *lower{value.lower_bound().value()}, + *upper{value.upper_bound().value()}; + std::size_t const lsz{ + lower == nullptr ? 0 : string_traits::size_buffer(*lower) - 1}, + usz{upper == nullptr ? 0 : string_traits::size_buffer(*upper) - 1}; + + if (value.empty()) + return std::size(s_empty) + 1; + else + return 1 + lsz + 1 + usz + 2; + } + +private: + static constexpr zview s_empty{"empty"_zv}; + static constexpr auto s_overrun{"Not enough space in buffer for range."_zv}; + + /// Compose error message for invalid range input. + static std::string err_bad_input(std::string_view text) + { + return internal::concat("Invalid range input: '", text, "'"); + } +}; + + +/// A range type does not have an innate null value. +template struct nullness> : no_null> +{}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/result b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/result new file mode 100644 index 000000000..523394b72 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/result @@ -0,0 +1,16 @@ +/** pqxx::result class and support classes. + * + * pqxx::result represents the set of result rows from a database query. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/result.hxx" + +// Now include some types which depend on result, but which the user will +// expect to see defined after including this header. +#include "pqxx/internal/result_iterator.hxx" +#include "pqxx/field.hxx" +#include "pqxx/internal/result_iter.hxx" + +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/result.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/result.hxx new file mode 100644 index 000000000..6c41cc096 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/result.hxx @@ -0,0 +1,335 @@ +/* Definitions for the pqxx::result class and support classes. + * + * pqxx::result represents the set of result rows from a database query. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/result instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_RESULT +#define PQXX_H_RESULT + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include +#include + +#include "pqxx/except.hxx" +#include "pqxx/types.hxx" +#include "pqxx/util.hxx" +#include "pqxx/zview.hxx" + +#include "pqxx/internal/encodings.hxx" + + +namespace pqxx::internal +{ +// TODO: Make noexcept (but breaks ABI). +PQXX_LIBEXPORT void clear_result(pq::PGresult const *); +} // namespace pqxx::internal + + +namespace pqxx::internal::gate +{ +class result_connection; +class result_creation; +class result_pipeline; +class result_row; +class result_sql_cursor; +} // namespace pqxx::internal::gate + + +namespace pqxx +{ +/// Result set containing data returned by a query or command. +/** This behaves as a container (as defined by the C++ standard library) and + * provides random access const iterators to iterate over its rows. You can + * also access a row by indexing a `result R` by the row's zero-based + * number: + * + * + * for (result::size_type i=0; i < std::size(R); ++i) Process(R[i]); + * + * + * Result sets in libpqxx are lightweight, reference-counted wrapper objects + * which are relatively small and cheap to copy. Think of a result object as + * a "smart pointer" to an underlying result set. + * + * @warning The result set that a result object points to is not thread-safe. + * If you copy a result object, it still refers to the same underlying result + * set. So never copy, destroy, query, or otherwise access a result while + * another thread may be copying, destroying, querying, or otherwise accessing + * the same result set--even if it is doing so through a different result + * object! + */ +class PQXX_LIBEXPORT result +{ +public: + using size_type = result_size_type; + using difference_type = result_difference_type; + using reference = row; + using const_iterator = const_result_iterator; + using pointer = const_iterator; + using iterator = const_iterator; + using const_reverse_iterator = const_reverse_result_iterator; + using reverse_iterator = const_reverse_iterator; + + result() noexcept : + m_data{make_data_pointer()}, + m_query{}, + m_encoding{internal::encoding_group::MONOBYTE} + {} + + result(result const &rhs) noexcept = default; + result(result &&rhs) noexcept = default; + + /// Assign one result to another. + /** Copying results is cheap: it copies only smart pointers, but the actual + * data stays in the same place. + */ + result &operator=(result const &rhs) noexcept = default; + + /// Assign one result to another, invaliding the old one. + result &operator=(result &&rhs) noexcept = default; + + /** + * @name Comparisons + * + * You can compare results for equality. Beware: this is a very strict, + * dumb comparison. The smallest difference between two results (such as a + * string "Foo" versus a string "foo") will make them unequal. + */ + //@{ + /// Compare two results for equality. + [[nodiscard]] bool operator==(result const &) const noexcept; + /// Compare two results for inequality. + [[nodiscard]] bool operator!=(result const &rhs) const noexcept + { + return not operator==(rhs); + } + //@} + + /// Iterate rows, reading them directly into a tuple of "TYPE...". + /** Converts the fields to values of the given respective types. + * + * Use this only with a ranged "for" loop. The iteration produces + * std::tuple which you can "unpack" to a series of `auto` + * variables. + */ + template auto iter() const; + + [[nodiscard]] const_reverse_iterator rbegin() const; + [[nodiscard]] const_reverse_iterator crbegin() const; + [[nodiscard]] const_reverse_iterator rend() const; + [[nodiscard]] const_reverse_iterator crend() const; + + [[nodiscard]] const_iterator begin() const noexcept; + [[nodiscard]] const_iterator cbegin() const noexcept; + [[nodiscard]] inline const_iterator end() const noexcept; + [[nodiscard]] inline const_iterator cend() const noexcept; + + [[nodiscard]] reference front() const noexcept; + [[nodiscard]] reference back() const noexcept; + + [[nodiscard]] PQXX_PURE size_type size() const noexcept; + [[nodiscard]] PQXX_PURE bool empty() const noexcept; + [[nodiscard]] size_type capacity() const noexcept { return size(); } + + /// Exchange two `result` values in an exception-safe manner. + /** If the swap fails, the two values will be exactly as they were before. + * + * The swap is not necessarily thread-safe. + */ + void swap(result &) noexcept; + + /// Index a row by number. + /** This returns a @ref row object. Generally you should not keep the row + * around as a variable, but if you do, make sure that your variable is a + * `row`, not a `row&`. + */ + [[nodiscard]] row operator[](size_type i) const noexcept; + +#if defined(PQXX_HAVE_MULTIDIMENSIONAL_SUBSCRIPT) + // TODO: If C++23 will let us, also accept string for the column. + [[nodiscard]] field + operator[](size_type row_num, row_size_type col_num) const noexcept; +#endif + + /// Index a row by number, but check that the row number is valid. + row at(size_type) const; + + /// Index a field by row number and column number. + field at(size_type, row_size_type) const; + + /// Let go of the result's data. + /** Use this if you need to deallocate the result data earlier than you can + * destroy the `result` object itself. + * + * Multiple `result` objects can refer to the same set of underlying data. + * The underlying data will be deallocated once all `result` objects that + * refer to it are cleared or destroyed. + */ + void clear() noexcept + { + m_data.reset(); + m_query = nullptr; + } + + /** + * @name Column information + */ + //@{ + /// Number of columns in result. + [[nodiscard]] PQXX_PURE row_size_type columns() const noexcept; + + /// Number of given column (throws exception if it doesn't exist). + [[nodiscard]] row_size_type column_number(zview name) const; + + /// Name of column with this number (throws exception if it doesn't exist) + [[nodiscard]] char const *column_name(row_size_type number) const &; + + /// Return column's type, as an OID from the system catalogue. + [[nodiscard]] oid column_type(row_size_type col_num) const; + + /// Return column's type, as an OID from the system catalogue. + [[nodiscard]] oid column_type(zview col_name) const + { + return column_type(column_number(col_name)); + } + + /// What table did this column come from? + [[nodiscard]] oid column_table(row_size_type col_num) const; + + /// What table did this column come from? + [[nodiscard]] oid column_table(zview col_name) const + { + return column_table(column_number(col_name)); + } + + /// What column in its table did this column come from? + [[nodiscard]] row_size_type table_column(row_size_type col_num) const; + + /// What column in its table did this column come from? + [[nodiscard]] row_size_type table_column(zview col_name) const + { + return table_column(column_number(col_name)); + } + //@} + + /// Query that produced this result, if available (empty string otherwise) + [[nodiscard]] PQXX_PURE std::string const &query() const &noexcept; + + /// If command was an `INSERT` of 1 row, return oid of the inserted row. + /** @return Identifier of inserted row if exactly one row was inserted, or + * @ref oid_none otherwise. + */ + [[nodiscard]] PQXX_PURE oid inserted_oid() const; + + /// If command was `INSERT`, `UPDATE`, or `DELETE`: number of affected rows. + /** @return Number of affected rows if last command was `INSERT`, `UPDATE`, + * or `DELETE`; zero for all other commands. + */ + [[nodiscard]] PQXX_PURE size_type affected_rows() const; + + // C++20: Concept like std::invocable, but without specifying param types. + /// Run `func` on each row, passing the row's fields as parameters. + /** Goes through the rows from first to last. You provide a callable `func`. + * + * For each row in the `result`, `for_each` will call `func`. It converts + * the row's fields to the types of `func`'s parameters, and pass them to + * `func`. + * + * (Therefore `func` must have a _single_ signature. It can't be a generic + * lambda, or an object of a class with multiple overloaded function call + * operators. Otherwise, `for_each` will have no way to detect a parameter + * list without ambiguity.) + * + * If any of your parameter types is `std::string_view`, it refers to the + * underlying storage of this `result`. + * + * If any of your parameter types is a reference type, its argument will + * refer to a temporary value which only lives for the duration of that + * single invocation to `func`. If the reference is an lvalue reference, it + * must be `const`. + * + * For example, this queries employee names and salaries from the database + * and prints how much each would like to earn instead: + * ```cxx + * tx.exec("SELECT name, salary FROM employee").for_each( + * [](std::string_view name, float salary){ + * std::cout << name << " would like " << salary * 2 << ".\n"; + * }) + * ``` + * + * If `func` throws an exception, processing stops at that point and + * propagates the exception. + * + * @throws usage_error if `func`'s number of parameters does not match the + * number of columns in this result. + */ + template inline void for_each(CALLABLE &&func) const; + +private: + using data_pointer = std::shared_ptr; + + /// Underlying libpq result set. + data_pointer m_data; + + /// Factory for data_pointer. + static data_pointer + make_data_pointer(internal::pq::PGresult const *res = nullptr) noexcept + { + return {res, internal::clear_result}; + } + + friend class pqxx::internal::gate::result_pipeline; + PQXX_PURE std::shared_ptr query_ptr() const noexcept + { + return m_query; + } + + /// Query string. + std::shared_ptr m_query; + + internal::encoding_group m_encoding; + + static std::string const s_empty_string; + + friend class pqxx::field; + // TODO: noexcept. Breaks ABI. + PQXX_PURE char const *get_value(size_type row, row_size_type col) const; + // TODO: noexcept. Breaks ABI. + PQXX_PURE bool get_is_null(size_type row, row_size_type col) const; + PQXX_PURE + field_size_type get_length(size_type, row_size_type) const noexcept; + + friend class pqxx::internal::gate::result_creation; + result( + internal::pq::PGresult *rhs, std::shared_ptr query, + internal::encoding_group enc); + + PQXX_PRIVATE void check_status(std::string_view desc = ""sv) const; + + friend class pqxx::internal::gate::result_connection; + friend class pqxx::internal::gate::result_row; + bool operator!() const noexcept { return m_data.get() == nullptr; } + operator bool() const noexcept { return m_data.get() != nullptr; } + + [[noreturn]] PQXX_PRIVATE void + throw_sql_error(std::string const &Err, std::string const &Query) const; + PQXX_PRIVATE PQXX_PURE int errorposition() const; + PQXX_PRIVATE std::string status_error() const; + + friend class pqxx::internal::gate::result_sql_cursor; + PQXX_PURE char const *cmd_status() const noexcept; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/robusttransaction b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/robusttransaction new file mode 100644 index 000000000..04b71d7cc --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/robusttransaction @@ -0,0 +1,8 @@ +/** pqxx::robusttransaction class. + * + * pqxx::robusttransaction is a slower but safer transaction class. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/robusttransaction.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/robusttransaction.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/robusttransaction.hxx new file mode 100644 index 000000000..faf6dbf5e --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/robusttransaction.hxx @@ -0,0 +1,120 @@ +/* Definition of the pqxx::robusttransaction class. + * + * pqxx::robusttransaction is a slower but safer transaction class. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/robusttransaction instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ROBUSTTRANSACTION +#define PQXX_H_ROBUSTTRANSACTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/dbtransaction.hxx" + +namespace pqxx::internal +{ +/// Helper base class for the @ref robusttransaction class template. +class PQXX_LIBEXPORT PQXX_NOVTABLE basic_robusttransaction + : public dbtransaction +{ +public: + virtual ~basic_robusttransaction() override = 0; + +protected: + basic_robusttransaction( + connection &c, zview begin_command, std::string_view tname); + basic_robusttransaction(connection &c, zview begin_command); + +private: + using IDType = unsigned long; + + std::string m_conn_string; + std::string m_xid; + int m_backendpid = -1; + + void init(zview begin_command); + + // @warning This function will become `final`. + virtual void do_commit() override; +}; +} // namespace pqxx::internal + + +namespace pqxx +{ +/** + * @ingroup transactions + * + * @{ + */ + +/// Slightly slower, better-fortified version of transaction. +/** Requires PostgreSQL 10 or better. + * + * robusttransaction is similar to transaction, but spends more time and effort + * to deal with the hopefully rare case that the connection to the backend is + * lost just while it's trying to commit. In such cases, the client does not + * know whether the backend (on the other side of the broken connection) + * managed to commit the transaction. + * + * When this happens, robusttransaction tries to reconnect to the database and + * figure out what happened. + * + * This service level was made optional since you may not want to pay the + * overhead where it is not necessary. Certainly the use of this class makes + * no sense for local connections, or for transactions that read the database + * but never modify it, or for noncritical database manipulations. + * + * Besides being slower, it's also more complex. Which means that in practice + * a robusttransaction could actually fail more instead of less often than a + * normal transaction. What robusttransaction tries to achieve is to give you + * certainty, not just be more successful per se. + */ +template +class robusttransaction final : public internal::basic_robusttransaction +{ +public: + /** Create robusttransaction of given name. + * @param c Connection inside which this robusttransaction should live. + * @param tname optional human-readable name for this transaction. + */ + robusttransaction(connection &c, std::string_view tname) : + internal::basic_robusttransaction{ + c, pqxx::internal::begin_cmd, + tname} + {} + + /** Create robusttransaction of given name. + * @param c Connection inside which this robusttransaction should live. + * @param tname optional human-readable name for this transaction. + */ + robusttransaction(connection &c, std::string &&tname) : + internal::basic_robusttransaction{ + c, pqxx::internal::begin_cmd, + std::move(tname)} + {} + + /** Create robusttransaction of given name. + * @param c Connection inside which this robusttransaction should live. + */ + explicit robusttransaction(connection &c) : + internal::basic_robusttransaction{ + c, pqxx::internal::begin_cmd} + {} + + virtual ~robusttransaction() noexcept override { close(); } +}; + +/** + * @} + */ +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/row b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/row new file mode 100644 index 000000000..62a950ac8 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/row @@ -0,0 +1,11 @@ +/** pqxx::row class. + * + * pqxx::row refers to a row in a result. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" + +#include "pqxx/result.hxx" +#include "pqxx/row.hxx" + +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/row.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/row.hxx new file mode 100644 index 000000000..5be5132e3 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/row.hxx @@ -0,0 +1,561 @@ +/* Definitions for the pqxx::result class and support classes. + * + * pqxx::result represents the set of result rows from a database query. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/result instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ROW +#define PQXX_H_ROW + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/except.hxx" +#include "pqxx/field.hxx" +#include "pqxx/result.hxx" + +#include "pqxx/internal/concat.hxx" + +namespace pqxx::internal +{ +template class result_iter; +} // namespace pqxx::internal + + +namespace pqxx +{ +/// Reference to one row in a result. +/** A row represents one row (also called a row) in a query result set. + * It also acts as a container mapping column numbers or names to field + * values (see below): + * + * ```cxx + * cout << row["date"].c_str() << ": " << row["name"].c_str() << endl; + * ``` + * + * The row itself acts like a (non-modifyable) container, complete with its + * own const_iterator and const_reverse_iterator. + */ +class PQXX_LIBEXPORT row +{ +public: + using size_type = row_size_type; + using difference_type = row_difference_type; + using const_iterator = const_row_iterator; + using iterator = const_iterator; + using reference = field; + using pointer = const_row_iterator; + using const_reverse_iterator = const_reverse_row_iterator; + using reverse_iterator = const_reverse_iterator; + + row() noexcept = default; + row(row &&) noexcept = default; + row(row const &) noexcept = default; + row &operator=(row const &) noexcept = default; + row &operator=(row &&) noexcept = default; + + /** + * @name Comparison + */ + //@{ + [[nodiscard]] PQXX_PURE bool operator==(row const &) const noexcept; + [[nodiscard]] bool operator!=(row const &rhs) const noexcept + { + return not operator==(rhs); + } + //@} + + [[nodiscard]] const_iterator begin() const noexcept; + [[nodiscard]] const_iterator cbegin() const noexcept; + [[nodiscard]] const_iterator end() const noexcept; + [[nodiscard]] const_iterator cend() const noexcept; + + /** + * @name Field access + */ + //@{ + [[nodiscard]] reference front() const noexcept; + [[nodiscard]] reference back() const noexcept; + + // TODO: noexcept. Breaks ABI. + [[nodiscard]] const_reverse_row_iterator rbegin() const; + // TODO: noexcept. Breaks ABI. + [[nodiscard]] const_reverse_row_iterator crbegin() const; + // TODO: noexcept. Breaks ABI. + [[nodiscard]] const_reverse_row_iterator rend() const; + // TODO: noexcept. Breaks ABI. + [[nodiscard]] const_reverse_row_iterator crend() const; + + [[nodiscard]] reference operator[](size_type) const noexcept; + /** Address field by name. + * @warning This is much slower than indexing by number, or iterating. + */ + [[nodiscard]] reference operator[](zview col_name) const; + + reference at(size_type) const; + /** Address field by name. + * @warning This is much slower than indexing by number, or iterating. + */ + reference at(zview col_name) const; + + [[nodiscard]] constexpr size_type size() const noexcept + { + return m_end - m_begin; + } + + [[deprecated("Swap iterators, not rows.")]] void swap(row &) noexcept; + + /// Row number, assuming this is a real row and not end()/rend(). + [[nodiscard]] constexpr result::size_type rownumber() const noexcept + { + return m_index; + } + + /** + * @name Column information + */ + //@{ + /// Number of given column (throws exception if it doesn't exist). + [[nodiscard]] size_type column_number(zview col_name) const; + + /// Return a column's type. + [[nodiscard]] oid column_type(size_type) const; + + /// Return a column's type. + [[nodiscard]] oid column_type(zview col_name) const + { + return column_type(column_number(col_name)); + } + + /// What table did this column come from? + [[nodiscard]] oid column_table(size_type col_num) const; + + /// What table did this column come from? + [[nodiscard]] oid column_table(zview col_name) const + { + return column_table(column_number(col_name)); + } + + /// What column number in its table did this result column come from? + /** A meaningful answer can be given only if the column in question comes + * directly from a column in a table. If the column is computed in any + * other way, a logic_error will be thrown. + * + * @param col_num a zero-based column number in this result set + * @return a zero-based column number in originating table + */ + [[nodiscard]] size_type table_column(size_type) const; + + /// What column number in its table did this result column come from? + [[nodiscard]] size_type table_column(zview col_name) const + { + return table_column(column_number(col_name)); + } + //@} + + [[nodiscard]] constexpr result::size_type num() const noexcept + { + return rownumber(); + } + + /** Produce a slice of this row, containing the given range of columns. + * + * @deprecated I haven't heard of anyone caring about row slicing at all in + * at least the last 15 years. Yet it adds complexity, so unless anyone + * files a bug explaining why they really need this feature, I'm going to + * remove it. Even if they do, the feature may need an update. + * + * The slice runs from the range's starting column to the range's end + * column, exclusive. It looks just like a normal result row, except + * slices can be empty. + */ + [[deprecated("Row slicing is going away. File a bug if you need it.")]] row + slice(size_type sbegin, size_type send) const; + + /// Is this a row without fields? Can only happen to a slice. + [[nodiscard, deprecated("Row slicing is going away.")]] PQXX_PURE bool + empty() const noexcept; + + /// Extract entire row's values into a tuple. + /** Converts to the types of the tuple's respective fields. + */ + template void to(Tuple &t) const + { + check_size(std::tuple_size_v); + convert(t); + } + + template std::tuple as() const + { + check_size(sizeof...(TYPE)); + using seq = std::make_index_sequence; + return get_tuple>(seq{}); + } + +protected: + friend class const_row_iterator; + friend class result; + row(result const &r, result_size_type index, size_type cols) noexcept; + + /// Throw @ref usage_error if row size is not `expected`. + void check_size(size_type expected) const + { + if (size() != expected) + throw usage_error{internal::concat( + "Tried to extract ", expected, " field(s) from a row of ", size(), + ".")}; + } + + /// Convert to a given tuple of values, don't check sizes. + /** We need this for cases where we have a full tuple of field types, but + * not a parameter pack. + */ + template TUPLE as_tuple() const + { + using seq = std::make_index_sequence>; + return get_tuple(seq{}); + } + + template friend class pqxx::internal::result_iter; + /// Convert entire row to tuple fields, without checking row size. + template void convert(Tuple &t) const + { + extract_fields(t, std::make_index_sequence>{}); + } + + friend class field; + + /// Result set of which this is one row. + result m_result; + + /// Row number. + /** + * You'd expect this to be unsigned, but due to the way reverse iterators + * are related to regular iterators, it must be allowed to underflow to -1. + */ + result::size_type m_index = 0; + + // TODO: Remove m_begin and (if possible) m_end when we remove slice(). + /// First column in slice. This row ignores lower-numbered columns. + size_type m_begin = 0; + /// End column in slice. This row only sees lower-numbered columns. + size_type m_end = 0; + +private: + template + void extract_fields(Tuple &t, std::index_sequence) const + { + (extract_value(t), ...); + } + + template + void extract_value(Tuple &t) const; + + /// Convert row's values as a new tuple. + template + auto get_tuple(std::index_sequence) const + { + return std::make_tuple(get_field()...); + } + + /// Extract and convert a field. + template auto get_field() const + { + return (*this)[index].as>(); + } +}; + + +/// Iterator for fields in a row. Use as row::const_iterator. +class PQXX_LIBEXPORT const_row_iterator : public field +{ +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = field const; + using pointer = field const *; + using size_type = row_size_type; + using difference_type = row_difference_type; + using reference = field; + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + const_row_iterator() = default; +#include "pqxx/internal/ignore-deprecated-post.hxx" + const_row_iterator(row const &t, row_size_type c) noexcept : + field{t.m_result, t.m_index, c} + {} + const_row_iterator(field const &F) noexcept : field{F} {} + const_row_iterator(const_row_iterator const &) noexcept = default; + const_row_iterator(const_row_iterator &&) noexcept = default; + + /** + * @name Dereferencing operators + */ + //@{ + [[nodiscard]] constexpr pointer operator->() const noexcept { return this; } + [[nodiscard]] reference operator*() const noexcept { return {*this}; } + //@} + + /** + * @name Manipulations + */ + //@{ + const_row_iterator &operator=(const_row_iterator const &) noexcept = default; + const_row_iterator &operator=(const_row_iterator &&) noexcept = default; + + // TODO: noexcept. Breaks ABI. + const_row_iterator operator++(int); + const_row_iterator &operator++() noexcept + { + ++m_col; + return *this; + } + // TODO: noexcept. Breaks ABI. + const_row_iterator operator--(int); + const_row_iterator &operator--() noexcept + { + --m_col; + return *this; + } + + const_row_iterator &operator+=(difference_type i) noexcept + { + m_col = size_type(difference_type(m_col) + i); + return *this; + } + const_row_iterator &operator-=(difference_type i) noexcept + { + m_col = size_type(difference_type(m_col) - i); + return *this; + } + //@} + + /** + * @name Comparisons + */ + //@{ + [[nodiscard]] constexpr bool + operator==(const_row_iterator const &i) const noexcept + { + return col() == i.col(); + } + [[nodiscard]] constexpr bool + operator!=(const_row_iterator const &i) const noexcept + { + return col() != i.col(); + } + [[nodiscard]] constexpr bool + operator<(const_row_iterator const &i) const noexcept + { + return col() < i.col(); + } + [[nodiscard]] constexpr bool + operator<=(const_row_iterator const &i) const noexcept + { + return col() <= i.col(); + } + [[nodiscard]] constexpr bool + operator>(const_row_iterator const &i) const noexcept + { + return col() > i.col(); + } + [[nodiscard]] constexpr bool + operator>=(const_row_iterator const &i) const noexcept + { + return col() >= i.col(); + } + //@} + + /** + * @name Arithmetic operators + */ + //@{ + [[nodiscard]] inline const_row_iterator + operator+(difference_type) const noexcept; + + friend const_row_iterator + operator+(difference_type, const_row_iterator const &) noexcept; + + [[nodiscard]] inline const_row_iterator + operator-(difference_type) const noexcept; + [[nodiscard]] inline difference_type + operator-(const_row_iterator const &) const noexcept; + //@} +}; + + +/// Reverse iterator for a row. Use as row::const_reverse_iterator. +class PQXX_LIBEXPORT const_reverse_row_iterator : private const_row_iterator +{ +public: + using super = const_row_iterator; + using iterator_type = const_row_iterator; + using iterator_type::difference_type; + using iterator_type::iterator_category; + using iterator_type::pointer; + using value_type = iterator_type::value_type; + using reference = iterator_type::reference; + + const_reverse_row_iterator() noexcept = default; + const_reverse_row_iterator(const_reverse_row_iterator const &) noexcept = + default; + const_reverse_row_iterator(const_reverse_row_iterator &&) noexcept = default; + + explicit const_reverse_row_iterator(super const &rhs) noexcept : + const_row_iterator{rhs} + { + super::operator--(); + } + + [[nodiscard]] PQXX_PURE iterator_type base() const noexcept; + + /** + * @name Dereferencing operators + */ + //@{ + using iterator_type::operator->; + using iterator_type::operator*; + //@} + + /** + * @name Manipulations + */ + //@{ + const_reverse_row_iterator & + operator=(const_reverse_row_iterator const &r) noexcept + { + iterator_type::operator=(r); + return *this; + } + const_reverse_row_iterator operator++() noexcept + { + iterator_type::operator--(); + return *this; + } + // TODO: noexcept. Breaks ABI. + const_reverse_row_iterator operator++(int); + const_reverse_row_iterator &operator--() noexcept + { + iterator_type::operator++(); + return *this; + } + const_reverse_row_iterator operator--(int); + // TODO: noexcept. Breaks ABI. + const_reverse_row_iterator &operator+=(difference_type i) noexcept + { + iterator_type::operator-=(i); + return *this; + } + const_reverse_row_iterator &operator-=(difference_type i) noexcept + { + iterator_type::operator+=(i); + return *this; + } + //@} + + /** + * @name Arithmetic operators + */ + //@{ + [[nodiscard]] const_reverse_row_iterator + operator+(difference_type i) const noexcept + { + return const_reverse_row_iterator{base() - i}; + } + [[nodiscard]] const_reverse_row_iterator + operator-(difference_type i) noexcept + { + return const_reverse_row_iterator{base() + i}; + } + [[nodiscard]] difference_type + operator-(const_reverse_row_iterator const &rhs) const noexcept + { + return rhs.const_row_iterator::operator-(*this); + } + //@} + + /** + * @name Comparisons + */ + //@{ + [[nodiscard]] bool + operator==(const_reverse_row_iterator const &rhs) const noexcept + { + return iterator_type::operator==(rhs); + } + [[nodiscard]] bool + operator!=(const_reverse_row_iterator const &rhs) const noexcept + { + return !operator==(rhs); + } + + [[nodiscard]] constexpr bool + operator<(const_reverse_row_iterator const &rhs) const noexcept + { + return iterator_type::operator>(rhs); + } + [[nodiscard]] constexpr bool + operator<=(const_reverse_row_iterator const &rhs) const noexcept + { + return iterator_type::operator>=(rhs); + } + [[nodiscard]] constexpr bool + operator>(const_reverse_row_iterator const &rhs) const noexcept + { + return iterator_type::operator<(rhs); + } + [[nodiscard]] constexpr bool + operator>=(const_reverse_row_iterator const &rhs) const noexcept + { + return iterator_type::operator<=(rhs); + } + //@} +}; + + +const_row_iterator +const_row_iterator::operator+(difference_type o) const noexcept +{ + // TODO:: More direct route to home().columns()? + return { + row{home(), idx(), home().columns()}, + size_type(difference_type(col()) + o)}; +} + +inline const_row_iterator operator+( + const_row_iterator::difference_type o, const_row_iterator const &i) noexcept +{ + return i + o; +} + +inline const_row_iterator +const_row_iterator::operator-(difference_type o) const noexcept +{ + // TODO:: More direct route to home().columns()? + return { + row{home(), idx(), home().columns()}, + size_type(difference_type(col()) - o)}; +} + +inline const_row_iterator::difference_type +const_row_iterator::operator-(const_row_iterator const &i) const noexcept +{ + return difference_type(num() - i.num()); +} + + +template +inline void row::extract_value(Tuple &t) const +{ + using field_type = strip_t(t))>; + field const f{m_result, m_index, index}; + std::get(t) = from_string(f); +} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/separated_list b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/separated_list new file mode 100644 index 000000000..1bdf51c6a --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/separated_list @@ -0,0 +1,6 @@ +/** Helper similar to Python's @c str.join(). + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/separated_list.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/separated_list.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/separated_list.hxx new file mode 100644 index 000000000..d4230ea08 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/separated_list.hxx @@ -0,0 +1,142 @@ +/* Helper similar to Python's `str.join()`. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/separated_list instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_SEPARATED_LIST +#define PQXX_H_SEPARATED_LIST + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + +#include "pqxx/strconv.hxx" + +// C++20: Simplify using std::ranges::range. +// C++20: Optimise buffer allocation using random_access_range/iterator. +namespace pqxx +{ +/** + * @defgroup utility Utility functions + */ +//@{ + +/// Represent sequence of values as a string, joined by a given separator. +/** + * Use this to turn e.g. the numbers 1, 2, and 3 into a string "1, 2, 3". + * + * @param sep separator string (to be placed between items) + * @param begin beginning of items sequence + * @param end end of items sequence + * @param access functor defining how to dereference sequence elements + */ +template +[[nodiscard]] inline std::string +separated_list(std::string_view sep, ITER begin, ITER end, ACCESS access) +{ + if (end == begin) + return {}; + auto next{begin}; + ++next; + if (next == end) + return to_string(access(begin)); + + // From here on, we've got at least 2 elements -- meaning that we need sep. + using elt_type = strip_t; + using traits = string_traits; + + std::size_t budget{0}; + for (ITER cnt{begin}; cnt != end; ++cnt) + budget += traits::size_buffer(access(cnt)); + budget += + static_cast(std::distance(begin, end)) * std::size(sep); + + std::string result; + result.resize(budget); + + char *const data{result.data()}; + char *here{data}; + char *stop{data + budget}; + here = traits::into_buf(here, stop, access(begin)) - 1; + for (++begin; begin != end; ++begin) + { + here += sep.copy(here, std::size(sep)); + here = traits::into_buf(here, stop, access(begin)) - 1; + } + result.resize(static_cast(here - data)); + return result; +} + + +/// Render sequence as a string, using given separator between items. +template +[[nodiscard]] inline std::string +separated_list(std::string_view sep, ITER begin, ITER end) +{ + return separated_list(sep, begin, end, [](ITER i) { return *i; }); +} + + +/// Render items in a container as a string, using given separator. +template +[[nodiscard]] inline auto +separated_list(std::string_view sep, CONTAINER const &c) + /* + Always std::string; necessary because SFINAE doesn't work with the + contents of function bodies, so the check for iterability has to be in + the signature. + */ + -> typename std::enable_if< + (not std::is_void::value and + not std::is_void::value), + std::string>::type +{ + return separated_list(sep, std::begin(c), std::end(c)); +} + + +/// Render items in a tuple as a string, using given separator. +template< + typename TUPLE, std::size_t INDEX = 0, typename ACCESS, + typename std::enable_if< + (INDEX == std::tuple_size::value - 1), int>::type = 0> +[[nodiscard]] inline std::string separated_list( + std::string_view /* sep */, TUPLE const &t, ACCESS const &access) +{ + return to_string(access(&std::get(t))); +} + +template< + typename TUPLE, std::size_t INDEX = 0, typename ACCESS, + typename std::enable_if< + (INDEX < std::tuple_size::value - 1), int>::type = 0> +[[nodiscard]] inline std::string +separated_list(std::string_view sep, TUPLE const &t, ACCESS const &access) +{ + std::string out{to_string(access(&std::get(t)))}; + out.append(sep); + out.append(separated_list(sep, t, access)); + return out; +} + +template< + typename TUPLE, std::size_t INDEX = 0, + typename std::enable_if< + (INDEX <= std::tuple_size::value), int>::type = 0> +[[nodiscard]] inline std::string +separated_list(std::string_view sep, TUPLE const &t) +{ + // TODO: Optimise allocation. + return separated_list(sep, t, [](TUPLE const &tup) { return *tup; }); +} +//@} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/strconv b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/strconv new file mode 100644 index 000000000..aa2c40ed5 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/strconv @@ -0,0 +1,6 @@ +/** String conversion definitions. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/strconv.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/strconv.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/strconv.hxx new file mode 100644 index 000000000..863711228 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/strconv.hxx @@ -0,0 +1,468 @@ +/* String conversion definitions. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/stringconv instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_STRCONV +#define PQXX_H_STRCONV + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include +#include +#include +#include + +#if __has_include() +# include +#endif + +#if defined(PQXX_HAVE_RANGES) && __has_include() +# include +#endif + +#include "pqxx/except.hxx" +#include "pqxx/util.hxx" +#include "pqxx/zview.hxx" + + +namespace pqxx::internal +{ +/// Attempt to demangle @c std::type_info::name() to something human-readable. +PQXX_LIBEXPORT std::string demangle_type_name(char const[]); +} // namespace pqxx::internal + + +namespace pqxx +{ +/** + * @defgroup stringconversion String conversion + * + * The PostgreSQL server accepts and represents data in string form. It has + * its own formats for various data types. The string conversions define how + * various C++ types translate to and from their respective PostgreSQL text + * representations. + * + * Each conversion is defined by a specialisations of @c string_traits. It + * gets complicated if you want top performance, but until you do, all you + * really need to care about when converting values between C++ in-memory + * representations such as @c int and the postgres string representations is + * the @c pqxx::to_string and @c pqxx::from_string functions. + * + * If you need to convert a type which is not supported out of the box, you'll + * need to define your own specialisations for these templates, similar to the + * ones defined here and in `pqxx/conversions.hxx`. Any conversion code which + * "sees" your specialisation will now support your conversion. In particular, + * you'll be able to read result fields into a variable of the new type. + * + * There is a macro to help you define conversions for individual enumeration + * types. The conversion will represent enumeration values as numeric strings. + */ +//@{ + +/// A human-readable name for a type, used in error messages and such. +/** Actually this may not always be very user-friendly. It uses + * @c std::type_info::name(). On gcc-like compilers we try to demangle its + * output. Visual Studio produces human-friendly names out of the box. + * + * This variable is not inline. Inlining it gives rise to "memory leak" + * warnings from asan, the address sanitizer, possibly from use of + * @c std::type_info::name. + */ +template +std::string const type_name{internal::demangle_type_name(typeid(TYPE).name())}; + + +/// Traits describing a type's "null value," if any. +/** Some C++ types have a special value or state which correspond directly to + * SQL's NULL. + * + * The @c nullness traits describe whether it exists, and whether a particular + * value is null. + */ +template struct nullness +{ + /// Does this type have a null value? + static bool has_null; + + /// Is this type always null? + static bool always_null; + + /// Is @c value a null? + static bool is_null(TYPE const &value); + + /// Return a null value. + /** Don't use this in generic code to compare a value and see whether it is + * null. Some types may have multiple null values which do not compare as + * equal, or may define a null value which is not equal to anything including + * itself, like in SQL. + */ + [[nodiscard]] static TYPE null(); +}; + + +/// Nullness traits describing a type which does not have a null value. +template struct no_null +{ + /// Does @c TYPE have a "built-in null value"? + /** For example, a pointer can equal @c nullptr, which makes a very natural + * representation of an SQL null value. For such types, the code sometimes + * needs to make special allowances. + * + * for most types, such as @c int or @c std::string, there is no built-in + * null. If you want to represent an SQL null value for such a type, you + * would have to wrap it in something that does have a null value. For + * example, you could use @c std::optional for "either an @c int or a + * null value." + */ + static constexpr bool has_null = false; + + /// Are all values of this type null? + /** There are a few special C++ types which are always null - mainly + * @c std::nullptr_t. + */ + static constexpr bool always_null = false; + + /// Does a given value correspond to an SQL null value? + /** Most C++ types, such as @c int or @c std::string, have no inherent null + * value. But some types such as C-style string pointers do have a natural + * equivalent to an SQL null. + */ + [[nodiscard]] static constexpr bool is_null(TYPE const &) noexcept + { + return false; + } +}; + + +/// Traits class for use in string conversions. +/** Specialize this template for a type for which you wish to add to_string + * and from_string support. + * + * String conversions are not meant to work for nulls. Check for null before + * converting a value of @c TYPE to a string, or vice versa. + */ +template struct string_traits +{ + /// Return a @c string_view representing value, plus terminating zero. + /** Produces a @c string_view containing the PostgreSQL string representation + * for @c value. + * + * Uses the space from @c begin to @c end as a buffer, if needed. The + * returned string may lie somewhere in that buffer, or it may be a + * compile-time constant, or it may be null if value was a null value. Even + * if the string is stored in the buffer, its @c begin() may or may not be + * the same as @c begin. + * + * The @c string_view is guaranteed to be valid as long as the buffer from + * @c begin to @c end remains accessible and unmodified. + * + * @throws pqxx::conversion_overrun if the provided buffer space may not be + * enough. For maximum performance, this is a conservative estimate. It may + * complain about a buffer which is actually large enough for your value, if + * an exact check gets too expensive. + */ + [[nodiscard]] static inline zview + to_buf(char *begin, char *end, TYPE const &value); + + /// Write value's string representation into buffer at @c begin. + /** Assumes that value is non-null. + * + * Writes value's string representation into the buffer, starting exactly at + * @c begin, and ensuring a trailing zero. Returns the address just beyond + * the trailing zero, so the caller could use it as the @c begin for another + * call to @c into_buf writing a next value. + */ + static inline char *into_buf(char *begin, char *end, TYPE const &value); + + /// Parse a string representation of a @c TYPE value. + /** Throws @c conversion_error if @c value does not meet the expected format + * for a value of this type. + */ + [[nodiscard]] static inline TYPE from_string(std::string_view text); + + // C++20: Can we make these all constexpr? + /// Estimate how much buffer space is needed to represent value. + /** The estimate may be a little pessimistic, if it saves time. + * + * The estimate includes the terminating zero. + */ + [[nodiscard]] static inline std::size_t + size_buffer(TYPE const &value) noexcept; +}; + + +/// Nullness: Enums do not have an inherent null value. +template +struct nullness>> : no_null +{}; +} // namespace pqxx + + +namespace pqxx::internal +{ +/// Helper class for defining enum conversions. +/** The conversion will convert enum values to numeric strings, and vice versa. + * + * To define a string conversion for an enum type, derive a @c string_traits + * specialisation for the enum from this struct. + * + * There's usually an easier way though: the @c PQXX_DECLARE_ENUM_CONVERSION + * macro. Use @c enum_traits manually only if you need to customise your + * traits type in more detail. + */ +template struct enum_traits +{ + using impl_type = std::underlying_type_t; + using impl_traits = string_traits; + + [[nodiscard]] static constexpr zview + to_buf(char *begin, char *end, ENUM const &value) + { + return impl_traits::to_buf(begin, end, to_underlying(value)); + } + + static constexpr char *into_buf(char *begin, char *end, ENUM const &value) + { + return impl_traits::into_buf(begin, end, to_underlying(value)); + } + + [[nodiscard]] static ENUM from_string(std::string_view text) + { + return static_cast(impl_traits::from_string(text)); + } + + [[nodiscard]] static std::size_t size_buffer(ENUM const &value) noexcept + { + return impl_traits::size_buffer(to_underlying(value)); + } + +private: + // C++23: Replace with std::to_underlying. + static constexpr impl_type to_underlying(ENUM const &value) noexcept + { + return static_cast(value); + } +}; +} // namespace pqxx::internal + + +/// Macro: Define a string conversion for an enum type. +/** This specialises the @c pqxx::string_traits template, so use it in the + * @c ::pqxx namespace. + * + * For example: + * + * #include + * #include + * enum X { xa, xb }; + * namespace pqxx { PQXX_DECLARE_ENUM_CONVERSION(x); } + * int main() { std::cout << pqxx::to_string(xa) << std::endl; } + */ +#define PQXX_DECLARE_ENUM_CONVERSION(ENUM) \ + template<> struct string_traits : pqxx::internal::enum_traits \ + {}; \ + template<> inline std::string const type_name { #ENUM } + + +namespace pqxx +{ +/// Parse a value in postgres' text format as a TYPE. +/** If the form of the value found in the string does not match the expected + * type, e.g. if a decimal point is found when converting to an integer type, + * the conversion fails. Overflows (e.g. converting "9999999999" to a 16-bit + * C++ type) are also treated as errors. If in some cases this behaviour + * should be inappropriate, convert to something bigger such as @c long @c int + * first and then truncate the resulting value. + * + * Only the simplest possible conversions are supported. Fancy features like + * hexadecimal or octal, spurious signs, or exponent notation won't work. + * Whitespace is not stripped away. Only the kinds of strings that come out of + * PostgreSQL and out of to_string() can be converted. + */ +template +[[nodiscard]] inline TYPE from_string(std::string_view text) +{ + return string_traits::from_string(text); +} + + +/// "Convert" a std::string_view to a std::string_view. +/** Just returns its input. + * + * @warning Of course the result is only valid for as long as the original + * string remains valid! Never access the string referenced by the return + * value after the original has been destroyed. + */ +template<> +[[nodiscard]] inline std::string_view from_string(std::string_view text) +{ + return text; +} + + +/// Attempt to convert postgres-generated string to given built-in object. +/** This is like the single-argument form of the function, except instead of + * returning the value, it sets @c value. + * + * You may find this more convenient in that it infers the type you want from + * the argument you pass. But there are disadvantages: it requires an + * assignment operator, and it may be less efficient. + */ +template inline void from_string(std::string_view text, T &value) +{ + value = from_string(text); +} + + +/// Convert a value to a readable string that PostgreSQL will understand. +/** The conversion does no special formatting, and ignores any locale settings. + * The resulting string will be human-readable and in a format suitable for use + * in SQL queries. It won't have niceties such as "thousands separators" + * though. + */ +template inline std::string to_string(TYPE const &value); + + +/// Convert multiple values to strings inside a single buffer. +/** There must be enough room for all values, or this will throw + * @c conversion_overrun. You can obtain a conservative estimate of the buffer + * space required by calling @c size_buffer() on the values. + * + * The @c std::string_view results may point into the buffer, so don't assume + * that they will remain valid after you destruct or move the buffer. + */ +template +[[nodiscard]] inline std::vector +to_buf(char *here, char const *end, TYPE... value) +{ + return {[&here, end](auto v) { + auto begin = here; + here = string_traits::into_buf(begin, end, v); + // Exclude the trailing zero out of the string_view. + auto len{static_cast(here - begin) - 1}; + return std::string_view{begin, len}; + }(value)...}; +} + +/// Convert a value to a readable string that PostgreSQL will understand. +/** This variant of to_string can sometimes save a bit of time in loops, by + * re-using a std::string for multiple conversions. + */ +template +inline void into_string(TYPE const &value, std::string &out); + + +/// Is @c value null? +template +[[nodiscard]] inline constexpr bool is_null(TYPE const &value) noexcept +{ + return nullness>::is_null(value); +} + + +/// Estimate how much buffer space is needed to represent values as a string. +/** The estimate may be a little pessimistic, if it saves time. It also + * includes room for a terminating zero after each value. + */ +template +[[nodiscard]] inline std::size_t size_buffer(TYPE const &...value) noexcept +{ + return (string_traits>::size_buffer(value) + ...); +} + + +/// Does this type translate to an SQL array? +/** Specialisations may override this to be true for container types. + * + * This may not always be a black-and-white choice. For instance, a + * @c std::string is a container, but normally it translates to an SQL string, + * not an SQL array. + */ +template inline constexpr bool is_sql_array{false}; + + +/// Can we use this type in arrays and composite types without quoting them? +/** Define this as @c true only if values of @c TYPE can never contain any + * special characters that might need escaping or confuse the parsing of array + * or composite * types, such as commas, quotes, parentheses, braces, newlines, + * and so on. + * + * When converting a value of such a type to a string in an array or a field in + * a composite type, we do not need to add quotes, nor escape any special + * characters. + * + * This is just an optimisation, so it defaults to @c false to err on the side + * of slow correctness. + */ +template inline constexpr bool is_unquoted_safe{false}; + + +/// Element separator between SQL array elements of this type. +template inline constexpr char array_separator{','}; + + +/// What's the preferred format for passing non-null parameters of this type? +/** This affects how we pass parameters of @c TYPE when calling parameterised + * statements or prepared statements. + * + * Generally we pass parameters in text format, but binary strings are the + * exception. We also pass nulls in binary format, so this function need not + * handle null values. + */ +template inline constexpr format param_format(TYPE const &) +{ + return format::text; +} + + +/// Implement @c string_traits::to_buf by calling @c into_buf. +/** When you specialise @c string_traits for a new type, most of the time its + * @c to_buf implementation has no special optimisation tricks and just writes + * its text into the buffer it receives from the caller, starting at the + * beginning. + * + * In that common situation, you can implement @c to_buf as just a call to + * @c generic_to_buf. It will call @c into_buf and return the right result for + * @c to_buf. + */ +template +inline zview generic_to_buf(char *begin, char *end, TYPE const &value) +{ + using traits = string_traits; + // The trailing zero does not count towards the zview's size, so subtract 1 + // from the result we get from into_buf(). + if (is_null(value)) + return {}; + else + return {begin, traits::into_buf(begin, end, value) - begin - 1}; +} + + +#if defined(PQXX_HAVE_CONCEPTS) +/// Concept: Binary string, akin to @c std::string for binary data. +/** Any type that satisfies this concept can represent an SQL BYTEA value. + * + * A @c binary has a @c begin(), @c end(), @c size(), and @data(). Each byte + * is a @c std::byte, and they must all be laid out contiguously in memory so + * we can reference them by a pointer. + */ +template +concept binary = std::ranges::contiguous_range and + std::is_same_v>, std::byte>; +#endif +//@} +} // namespace pqxx + + +#include "pqxx/internal/conversions.hxx" +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_from b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_from new file mode 100644 index 000000000..972762443 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_from @@ -0,0 +1,8 @@ +/** pqxx::stream_from class. + * + * pqxx::stream_from enables optimized batch reads from a database table. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/stream_from.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_from.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_from.hxx new file mode 100644 index 000000000..ff4a93d2e --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_from.hxx @@ -0,0 +1,361 @@ +/* Definition of the pqxx::stream_from class. + * + * pqxx::stream_from enables optimized batch reads from a database table. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/stream_from instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_STREAM_FROM +#define PQXX_H_STREAM_FROM + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include + +#include "pqxx/connection.hxx" +#include "pqxx/except.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/encoding_group.hxx" +#include "pqxx/internal/stream_iterator.hxx" +#include "pqxx/separated_list.hxx" +#include "pqxx/transaction_focus.hxx" + + +namespace pqxx +{ +class transaction_base; + + +/// Pass this to a `stream_from` constructor to stream table contents. +/** @deprecated Use @ref stream_from::table() instead. + */ +constexpr from_table_t from_table; +/// Pass this to a `stream_from` constructor to stream query results. +/** @deprecated Use stream_from::query() instead. + */ +constexpr from_query_t from_query; + + +/// Stream data from the database. +/** For larger data sets, retrieving data this way is likely to be faster than + * executing a query and then iterating and converting the rows fields. You + * will also be able to start processing before all of the data has come in. + * + * There are also downsides. Not all kinds of query will work in a stream. + * But straightforward `SELECT` and `UPDATE ... RETURNING` queries should work. + * This function makes use of @ref pqxx::stream_from, which in turn uses + * PostgreSQL's `COPY` command, so see the documentation for those to get the + * full details. + * + * There are other downsides. If there stream encounters an error, it may + * leave the entire connection in an unusable state, so you'll have to give the + * whole thing up. Finally, opening a stream puts the connection in a special + * state, so you won't be able to do many other things with the connection or + * the transaction while the stream is open. + * + * There are two ways of starting a stream: you stream either all rows in a + * table (using one of the factories, `table()` or `raw_table()`), or the + * results of a query (using the `query()` factory). + * + * Usually you'll want the `stream` convenience wrapper in + * @ref transaction_base, * so you don't need to deal with this class directly. + * + * @warning While a stream is active, you cannot execute queries, open a + * pipeline, etc. on the same transaction. A transaction can have at most one + * object of a type derived from @ref pqxx::transaction_focus active on it at a + * time. + */ +class PQXX_LIBEXPORT stream_from : transaction_focus +{ +public: + using raw_line = + std::pair>, std::size_t>; + + /// Factory: Execute query, and stream the results. + /** The query can be a SELECT query or a VALUES query; or it can be an + * UPDATE, INSERT, or DELETE with a RETURNING clause. + * + * The query is executed as part of a COPY statement, so there are additional + * restrictions on what kind of query you can use here. See the PostgreSQL + * documentation for the COPY command: + * + * https://www.postgresql.org/docs/current/sql-copy.html + */ + static stream_from query(transaction_base &tx, std::string_view q) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return {tx, from_query, q}; +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /** + * @name Streaming data from tables + * + * You can use `stream_from` to read a table's contents. This is a quick + * and easy way to read a table, but it comes with limitations. It cannot + * stream from a view, only from a table. It does not support conditions. + * And there are no guarantees about ordering. If you need any of those + * things, consider streaming from a query instead. + */ + //@{ + + /// Factory: Stream data from a pre-quoted table and columns. + /** Use this factory if you need to create multiple streams using the same + * table path and/or columns list, and you want to save a bit of work on + * composing the internal SQL statement for starting the stream. It lets you + * compose the string representations for the table path and the columns + * list, so you can compute these once and then re-use them later. + * + * @param tx The transaction within which the stream will operate. + * @param path Name or path for the table upon which the stream will + * operate. If any part of the table path may contain special + * characters or be case-sensitive, quote the path using + * pqxx::connection::quote_table(). + * @param columns Columns which the stream will read. They should be + * comma-separated and, if needed, quoted. You can produce the string + * using pqxx::connection::quote_columns(). If you omit this argument, + * the stream will read all columns in the table, in schema order. + */ + static stream_from raw_table( + transaction_base &tx, std::string_view path, + std::string_view columns = ""sv); + + /// Factory: Stream data from a given table. + /** This is the convenient way to stream from a table. + */ + static stream_from table( + transaction_base &tx, table_path path, + std::initializer_list columns = {}); + //@} + + /// Execute query, and stream over the results. + /** @deprecated Use factory function @ref query instead. + */ + [[deprecated("Use query() factory instead.")]] stream_from( + transaction_base &, from_query_t, std::string_view query); + + /// Stream all rows in table, all columns. + /** @deprecated Use factories @ref table or @ref raw_table instead. + */ + [[deprecated("Use table() or raw_table() factory instead.")]] stream_from( + transaction_base &, from_table_t, std::string_view table); + + /// Stream given columns from all rows in table. + /** @deprecated Use factories @ref table or @ref raw_table instead. + */ + template + [[deprecated("Use table() or raw_table() factory instead.")]] stream_from( + transaction_base &, from_table_t, std::string_view table, + Iter columns_begin, Iter columns_end); + + /// Stream given columns from all rows in table. + /** @deprecated Use factory function @ref query instead. + */ + template + [[deprecated("Use table() or raw_table() factory instead.")]] stream_from( + transaction_base &tx, from_table_t, std::string_view table, + Columns const &columns); + +#include "pqxx/internal/ignore-deprecated-pre.hxx" + /// @deprecated Use factories @ref table or @ref raw_table instead. + [[deprecated("Use the from_table_t overload instead.")]] stream_from( + transaction_base &tx, std::string_view table) : + stream_from{tx, from_table, table} + {} +#include "pqxx/internal/ignore-deprecated-post.hxx" + + /// @deprecated Use factories @ref table or @ref raw_table instead. + template + [[deprecated("Use the from_table_t overload instead.")]] stream_from( + transaction_base &tx, std::string_view table, Columns const &columns) : + stream_from{tx, from_table, table, columns} + {} + + /// @deprecated Use factories @ref table or @ref raw_table instead. + template + [[deprecated("Use the from_table_t overload instead.")]] stream_from( + transaction_base &, std::string_view table, Iter columns_begin, + Iter columns_end); + + ~stream_from() noexcept; + + /// May this stream still produce more data? + [[nodiscard]] constexpr operator bool() const noexcept + { + return not m_finished; + } + /// Has this stream produced all the data it is going to produce? + [[nodiscard]] constexpr bool operator!() const noexcept + { + return m_finished; + } + + /// Finish this stream. Call this before continuing to use the connection. + /** Consumes all remaining lines, and closes the stream. + * + * This may take a while if you're abandoning the stream before it's done, so + * skip it in error scenarios where you're not planning to use the connection + * again afterwards. + */ + void complete(); + + /// Read one row into a tuple. + /** Converts the row's fields into the fields making up the tuple. + * + * For a column which can contain nulls, be sure to give the corresponding + * tuple field a type which can be null. For example, to read a field as + * `int` when it may contain nulls, read it as `std::optional`. + * Using `std::shared_ptr` or `std::unique_ptr` will also work. + */ + template stream_from &operator>>(Tuple &); + + /// Doing this with a `std::variant` is going to be horrifically borked. + template + stream_from &operator>>(std::variant &) = delete; + + /// Iterate over this stream. Supports range-based "for" loops. + /** Produces an input iterator over the stream. + * + * Do not call this yourself. Use it like "for (auto data : stream.iter())". + */ + template [[nodiscard]] auto iter() & + { + return pqxx::internal::stream_input_iteration{*this}; + } + + /// Read a row. Return fields as views, valid until you read the next row. + /** Returns `nullptr` when there are no more rows to read. Do not attempt + * to read any further rows after that. + * + * Do not access the vector, or the storage referenced by the views, after + * closing or completing the stream, or after attempting to read a next row. + * + * A @ref pqxx::zview is like a `std::string_view`, but with the added + * guarantee that if its data pointer is non-null, the string is followed by + * a terminating zero (which falls just outside the view itself). + * + * If any of the views' data pointer is null, that means that the + * corresponding SQL field is null. + * + * @warning The return type may change in the future, to support C++20 + * coroutine-based usage. + */ + std::vector const *read_row() &; + + /// Read a raw line of text from the COPY command. + /** @warning Do not use this unless you really know what you're doing. */ + raw_line get_raw_line(); + +private: + // TODO: Clean up this signature once we cull the deprecated constructors. + /// @deprecated + stream_from( + transaction_base &tx, std::string_view table, std::string_view columns, + from_table_t); + + // TODO: Clean up this signature once we cull the deprecated constructors. + /// @deprecated + stream_from( + transaction_base &, std::string_view unquoted_table, + std::string_view columns, from_table_t, int); + + template + void extract_fields(Tuple &t, std::index_sequence) const + { + (extract_value(t), ...); + } + + pqxx::internal::glyph_scanner_func *m_glyph_scanner; + + /// Current row's fields' text, combined into one reusable string. + std::string m_row; + + /// The current row's fields. + std::vector m_fields; + + bool m_finished = false; + + void close(); + + template + void extract_value(Tuple &) const; + + /// Read a line of COPY data, write `m_row` and `m_fields`. + void parse_line(); +}; + + +template +inline stream_from::stream_from( + transaction_base &tx, from_table_t, std::string_view table_name, + Columns const &columns) : + stream_from{ + tx, from_table, table_name, std::begin(columns), std::end(columns)} +{} + + +template +inline stream_from::stream_from( + transaction_base &tx, from_table_t, std::string_view table, + Iter columns_begin, Iter columns_end) : + stream_from{ + tx, table, separated_list(",", columns_begin, columns_end), + from_table, 1} +{} + + +template inline stream_from &stream_from::operator>>(Tuple &t) +{ + if (m_finished) + return *this; + static constexpr auto tup_size{std::tuple_size_v}; + m_fields.reserve(tup_size); + parse_line(); + if (m_finished) + return *this; + + if (std::size(m_fields) != tup_size) + throw usage_error{internal::concat( + "Tried to extract ", tup_size, " field(s) from a stream of ", + std::size(m_fields), ".")}; + + extract_fields(t, std::make_index_sequence{}); + return *this; +} + + +template +inline void stream_from::extract_value(Tuple &t) const +{ + using field_type = strip_t(t))>; + using nullity = nullness; + assert(index < std::size(m_fields)); + if constexpr (nullity::always_null) + { + if (std::data(m_fields[index]) != nullptr) + throw conversion_error{"Streaming non-null value into null field."}; + } + else if (std::data(m_fields[index]) == nullptr) + { + if constexpr (nullity::has_null) + std::get(t) = nullity::null(); + else + internal::throw_null_conversion(type_name); + } + else + { + // Don't ever try to convert a non-null value to nullptr_t! + std::get(t) = from_string(m_fields[index]); + } +} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_to b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_to new file mode 100644 index 000000000..8760cf1f4 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_to @@ -0,0 +1,8 @@ +/** pqxx::stream_to class. + * + * pqxx::stream_to enables optimized batch updates to a database table. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/stream_to.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_to.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_to.hxx new file mode 100644 index 000000000..2a49d8f85 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/stream_to.hxx @@ -0,0 +1,455 @@ +/* Definition of the pqxx::stream_to class. + * + * pqxx::stream_to enables optimized batch updates to a database table. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/stream_to.hxx instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_STREAM_TO +#define PQXX_H_STREAM_TO + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/separated_list.hxx" +#include "pqxx/transaction_base.hxx" + + +namespace pqxx +{ +/// Efficiently write data directly to a database table. +/** If you wish to insert rows of data into a table, you can compose INSERT + * statements and execute them. But it's slow and tedious, and you need to + * worry about quoting and escaping the data. + * + * If you're just inserting a single row, it probably won't matter much. You + * can use prepared or parameterised statements to take care of the escaping + * for you. But if you're inserting large numbers of rows you will want + * something better. + * + * Inserting rows one by one using INSERT statements involves a lot of + * pointless overhead, especially when you are working with a remote database + * server over the network. You may end up sending each row over the network + * as a separate query, and waiting for a reply. Do it "in bulk" using + * `stream_to`, and you may find that it goes many times faster. Sometimes + * you gain orders of magnitude in speed. + * + * Here's how it works: you create a `stream_to` stream to start writing to + * your table. You will probably want to specify the columns. Then, you + * feed your data into the stream one row at a time. And finally, you call the + * stream's @ref complete function to tell it to finalise the operation, wait + * for completion, and check for errors. + * + * (You _must_ complete the stream before committing or aborting the + * transaction. The connection is in a special state while the stream is + * active, where it can't process commands, and can't commit or abort a + * transaction.) + * + * So how do you feed a row of data into the stream? There's several ways, but + * the preferred one is to call its @ref write_values. Pass the field values + * as arguments. Doesn't matter what type they are, as long as libpqxx knows + * how to convert them to PostgreSQL's text format: `int`, `std::string` or + * `std:string_view`, `float` and `double`, `bool`... lots of basic types + * are supported. If some of the values are null, feel free to use + * `std::optional`, `std::shared_ptr`, or `std::unique_ptr`. + * + * The arguments' types don't even have to match the fields' SQL types. If you + * want to insert an `int` into a `DECIMAL` column, that's your choice -- it + * will produce a `DECIMAL` value which happens to be integral. Insert a + * `float` into a `VARCHAR` column? That's fine, you'll get a string whose + * contents happen to read like a number. And so on. You can even insert + * different types of value in the same column on different rows. If you have + * a code path where a particular field is always null, just insert `nullptr`. + * + * There is another way to insert rows: the `<<` ("shift-left") operator. + * It's not as fast and it doesn't support variable arguments: each row must be + * either a `std::tuple` or something iterable, such as a `std::vector`, or + * anything else with a `begin()` and `end()`. + * + * @warning While a stream is active, you cannot execute queries, open a + * pipeline, etc. on the same transaction. A transaction can have at most one + * object of a type derived from @ref pqxx::transaction_focus active on it at a + * time. + */ +class PQXX_LIBEXPORT stream_to : transaction_focus +{ +public: + /// Stream data to a pre-quoted table and columns. + /** This factory can be useful when it's not convenient to provide the + * columns list in the form of a `std::initializer_list`, or when the list + * of columns is simply not known at compile time. + * + * Also use this if you need to create multiple streams using the same table + * path and/or columns list, and you want to save a bit of work on composing + * the internal SQL statement for starting the stream. It lets you compose + * the string representations for the table path and the columns list, so you + * can compute these once and then re-use them later. + * + * @param tx The transaction within which the stream will operate. + * @param path Name or path for the table upon which the stream will + * operate. If any part of the table path may contain special + * characters or be case-sensitive, quote the path using + * pqxx::connection::quote_table(). + * @param columns Columns to which the stream will write. They should be + * comma-separated and, if needed, quoted. You can produce the string + * using pqxx::connection::quote_columns(). If you omit this argument, + * the stream will write all columns in the table, in schema order. + */ + static stream_to raw_table( + transaction_base &tx, std::string_view path, std::string_view columns = "") + { + return {tx, path, columns}; + } + + /// Create a `stream_to` writing to a named table and columns. + /** Use this to stream data to a table, where the list of columns is known at + * compile time. + * + * @param tx The transaction within which the stream will operate. + * @param path A @ref table_path designating the target table. + * @param columns Optionally, the columns to which the stream should write. + * If you do not pass this, the stream will write to all columns in the + * table, in schema order. + */ + static stream_to table( + transaction_base &tx, table_path path, + std::initializer_list columns = {}) + { + auto const &conn{tx.conn()}; + return raw_table(tx, conn.quote_table(path), conn.quote_columns(columns)); + } + +#if defined(PQXX_HAVE_CONCEPTS) + /// Create a `stream_to` writing to a named table and columns. + /** Use this version to stream data to a table, when the list of columns is + * not known at compile time. + * + * @param tx The transaction within which the stream will operate. + * @param path A @ref table_path designating the target table. + * @param columns The columns to which the stream should write. + */ + template + static stream_to + table(transaction_base &tx, table_path path, COLUMNS const &columns) + { + auto const &conn{tx.conn()}; + return stream_to::raw_table( + tx, conn.quote_table(path), tx.conn().quote_columns(columns)); + } + + /// Create a `stream_to` writing to a named table and columns. + /** Use this version to stream data to a table, when the list of columns is + * not known at compile time. + * + * @param tx The transaction within which the stream will operate. + * @param path A @ref table_path designating the target table. + * @param columns The columns to which the stream should write. + */ + template + static stream_to + table(transaction_base &tx, std::string_view path, COLUMNS const &columns) + { + return stream_to::raw_table(tx, path, tx.conn().quote_columns(columns)); + } +#endif // PQXX_HAVE_CONCEPTS + + /// Create a stream, without specifying columns. + /** @deprecated Use @ref table or @ref raw_table as a factory. + * + * Fields will be inserted in whatever order the columns have in the + * database. + * + * You'll probably want to specify the columns, so that the mapping between + * your data fields and the table is explicit in your code, and not hidden + * in an "implicit contract" between your code and your schema. + */ + [[deprecated("Use table() or raw_table() factory.")]] stream_to( + transaction_base &tx, std::string_view table_name) : + stream_to{tx, table_name, ""sv} + {} + + /// Create a stream, specifying column names as a container of strings. + /** @deprecated Use @ref table or @ref raw_table as a factory. + */ + template + [[deprecated("Use table() or raw_table() factory.")]] stream_to( + transaction_base &, std::string_view table_name, Columns const &columns); + + /// Create a stream, specifying column names as a sequence of strings. + /** @deprecated Use @ref table or @ref raw_table as a factory. + */ + template + [[deprecated("Use table() or raw_table() factory.")]] stream_to( + transaction_base &, std::string_view table_name, Iter columns_begin, + Iter columns_end); + + ~stream_to() noexcept; + + /// Does this stream still need to @ref complete()? + [[nodiscard]] constexpr operator bool() const noexcept + { + return not m_finished; + } + /// Has this stream been through its concluding @c complete()? + [[nodiscard]] constexpr bool operator!() const noexcept + { + return m_finished; + } + + /// Complete the operation, and check for errors. + /** Always call this to close the stream in an orderly fashion, even after + * an error. (In the case of an error, abort the transaction afterwards.) + * + * The only circumstance where it's safe to skip this is after an error, if + * you're discarding the entire connection. + */ + void complete(); + + /// Insert a row of data. + /** Returns a reference to the stream, so you can chain the calls. + * + * The @c row can be a tuple, or any type that can be iterated. Each + * item becomes a field in the row, in the same order as the columns you + * specified when creating the stream. + * + * If you don't already happen to have your fields in the form of a tuple or + * container, prefer @c write_values. It's faster and more convenient. + */ + template stream_to &operator<<(Row const &row) + { + write_row(row); + return *this; + } + + /// Stream a `stream_from` straight into a `stream_to`. + /** This can be useful when copying between different databases. If the + * source and the destination are on the same database, you'll get better + * performance doing it all in a regular query. + */ + stream_to &operator<<(stream_from &); + + /// Insert a row of data, given in the form of a @c std::tuple or container. + /** The @c row can be a tuple, or any type that can be iterated. Each + * item becomes a field in the row, in the same order as the columns you + * specified when creating the stream. + * + * The preferred way to insert a row is @c write_values. + */ + template void write_row(Row const &row) + { + fill_buffer(row); + write_buffer(); + } + + /// Insert values as a row. + /** This is the recommended way of inserting data. Pass your field values, + * of any convertible type. + */ + template void write_values(Ts const &...fields) + { + fill_buffer(fields...); + write_buffer(); + } + +private: + /// Stream a pre-quoted table name and columns list. + stream_to( + transaction_base &tx, std::string_view path, std::string_view columns); + + bool m_finished = false; + + /// Reusable buffer for a row. Saves doing an allocation for each row. + std::string m_buffer; + + /// Reusable buffer for converting/escaping a field. + std::string m_field_buf; + + /// Glyph scanner, for parsing the client encoding. + internal::glyph_scanner_func *m_scanner; + + /// Write a row of raw text-format data into the destination table. + void write_raw_line(std::string_view); + + /// Write a row of data from @c m_buffer into the destination table. + /** Resets the buffer for the next row. + */ + void write_buffer(); + + /// COPY encoding for a null field, plus subsequent separator. + static constexpr std::string_view null_field{"\\N\t"}; + + /// Estimate buffer space needed for a field which is always null. + template + static std::enable_if_t::always_null, std::size_t> + estimate_buffer(T const &) + { + return std::size(null_field); + } + + /// Estimate buffer space needed for field f. + /** The estimate is not very precise. We don't actually know how much space + * we'll need once the escaping comes in. + */ + template + static std::enable_if_t::always_null, std::size_t> + estimate_buffer(T const &field) + { + return is_null(field) ? std::size(null_field) : size_buffer(field); + } + + /// Append escaped version of @c data to @c m_buffer, plus a tab. + void escape_field_to_buffer(std::string_view data); + + /// Append string representation for @c f to @c m_buffer. + /** This is for the general case, where the field may contain a value. + * + * Also appends a tab. The tab is meant to be a separator, not a terminator, + * so if you write any fields at all, you'll end up with one tab too many + * at the end of the buffer. + */ + template + std::enable_if_t::always_null> + append_to_buffer(Field const &f) + { + // We append each field, terminated by a tab. That will leave us with + // one tab too many, assuming we write any fields at all; we remove that + // at the end. + if (is_null(f)) + { + // Easy. Append null and tab in one go. + m_buffer.append(null_field); + } + else + { + // Convert f into m_buffer. + + using traits = string_traits; + auto const budget{estimate_buffer(f)}; + auto const offset{std::size(m_buffer)}; + + if constexpr (std::is_arithmetic_v) + { + // Specially optimised for "safe" types, which never need any + // escaping. Convert straight into m_buffer. + + // The budget we get from size_buffer() includes room for the trailing + // zero, which we must remove. But we're also inserting tabs between + // fields, so we re-purpose the extra byte for that. + auto const total{offset + budget}; + m_buffer.resize(total); + auto const data{m_buffer.data()}; + char *const end{traits::into_buf(data + offset, data + total, f)}; + *(end - 1) = '\t'; + // Shrink to fit. Keep the tab though. + m_buffer.resize(static_cast(end - data)); + } + else if constexpr ( + std::is_same_v or + std::is_same_v or + std::is_same_v) + { + // This string may need escaping. + m_field_buf.resize(budget); + escape_field_to_buffer(f); + } + else + { + // This field needs to be converted to a string, and after that, + // escaped as well. + m_field_buf.resize(budget); + auto const data{m_field_buf.data()}; + escape_field_to_buffer( + traits::to_buf(data, data + std::size(m_field_buf), f)); + } + } + } + + /// Append string representation for a null field to @c m_buffer. + /** This special case is for types which are always null. + * + * Also appends a tab. The tab is meant to be a separator, not a terminator, + * so if you write any fields at all, you'll end up with one tab too many + * at the end of the buffer. + */ + template + std::enable_if_t::always_null> + append_to_buffer(Field const &) + { + m_buffer.append(null_field); + } + + /// Write raw COPY line into @c m_buffer, based on a container of fields. + template + std::enable_if_t> + fill_buffer(Container const &c) + { + // To avoid unnecessary allocations and deallocations, we run through c + // twice: once to determine how much buffer space we may need, and once to + // actually write it into the buffer. + std::size_t budget{0}; + for (auto const &f : c) budget += estimate_buffer(f); + m_buffer.reserve(budget); + for (auto const &f : c) append_to_buffer(f); + } + + /// Estimate how many buffer bytes we need to write tuple. + template + static std::size_t + budget_tuple(Tuple const &t, std::index_sequence) + { + return (estimate_buffer(std::get(t)) + ...); + } + + /// Write tuple of fields to @c m_buffer. + template + void append_tuple(Tuple const &t, std::index_sequence) + { + (append_to_buffer(std::get(t)), ...); + } + + /// Write raw COPY line into @c m_buffer, based on a tuple of fields. + template void fill_buffer(std::tuple const &t) + { + using indexes = std::make_index_sequence; + + m_buffer.reserve(budget_tuple(t, indexes{})); + append_tuple(t, indexes{}); + } + + /// Write raw COPY line into @c m_buffer, based on varargs fields. + template void fill_buffer(const Ts &...fields) + { + (..., append_to_buffer(fields)); + } + + constexpr static std::string_view s_classname{"stream_to"}; +}; + + +template +inline stream_to::stream_to( + transaction_base &tx, std::string_view table_name, Columns const &columns) : + stream_to{tx, table_name, std::begin(columns), std::end(columns)} +{} + + +template +inline stream_to::stream_to( + transaction_base &tx, std::string_view table_name, Iter columns_begin, + Iter columns_end) : + stream_to{ + tx, + tx.quote_name( + table_name, + separated_list(",", columns_begin, columns_end, [&tx](auto col) { + return tx.quote_name(*col); + }))} +{} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/subtransaction b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/subtransaction new file mode 100644 index 000000000..e0d154903 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/subtransaction @@ -0,0 +1,8 @@ +/** pqxx::subtransaction class. + * + * pqxx::subtransaction is a nested transaction, i.e. one inside a transaction. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/subtransaction.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/subtransaction.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/subtransaction.hxx new file mode 100644 index 000000000..e66b7a7a8 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/subtransaction.hxx @@ -0,0 +1,96 @@ +/* Definition of the pqxx::subtransaction class. + * + * pqxx::subtransaction is a nested transaction, i.e. one within a transaction. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/subtransaction instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_SUBTRANSACTION +#define PQXX_H_SUBTRANSACTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/dbtransaction.hxx" + +namespace pqxx +{ +/** + * @ingroup transactions + */ +/// "Transaction" nested within another transaction +/** A subtransaction can be executed inside a backend transaction, or inside + * another subtransaction. This can be useful when, for example, statements in + * a transaction may harmlessly fail and you don't want them to abort the + * entire transaction. Here's an example of how a temporary table may be + * dropped before re-creating it, without failing if the table did not exist: + * + * ```cxx + * void do_job(connection &C) + * { + * string const temptable = "fleetingtable"; + * + * work W(C, "do_job"); + * do_firstpart(W); + * + * // Attempt to delete our temporary table if it already existed. + * try + * { + * subtransaction S(W, "droptemp"); + * S.exec0("DROP TABLE " + temptable); + * S.commit(); + * } + * catch (undefined_table const &) + * { + * // Table did not exist. Which is what we were hoping to achieve anyway. + * // Carry on without regrets. + * } + * + * // S may have gone into a failed state and been destroyed, but the + * // upper-level transaction W is still fine. We can continue to use it. + * W.exec0("CREATE TEMP TABLE " + temptable + "(bar integer, splat + * varchar)"); + * + * do_lastpart(W); + * } + * ``` + * + * (This is just an example. If you really wanted to do drop a table without + * an error if it doesn't exist, you'd use DROP TABLE IF EXISTS.) + * + * There are no isolation levels inside a transaction. They are not needed + * because all actions within the same backend transaction are always performed + * sequentially anyway. + * + * @warning While the subtransaction is "live," you cannot execute queries or + * open streams etc. on its parent transaction. A transaction can have at most + * one object of a type derived from @ref pqxx::transaction_focus active on it + * at a time. + */ +class PQXX_LIBEXPORT subtransaction : public transaction_focus, + public dbtransaction +{ +public: + /// Nest a subtransaction nested in another transaction. + explicit subtransaction(dbtransaction &t, std::string_view tname = ""sv); + + /// Nest a subtransaction in another subtransaction. + explicit subtransaction(subtransaction &t, std::string_view name = ""sv); + + virtual ~subtransaction() noexcept override; + +private: + std::string quoted_name() const + { + return quote_name(transaction_focus::name()); + } + virtual void do_commit() override; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/time b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/time new file mode 100644 index 000000000..85df05744 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/time @@ -0,0 +1,6 @@ +/** Date/time string conversions. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/time.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/time.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/time.hxx new file mode 100644 index 000000000..effed05e0 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/time.hxx @@ -0,0 +1,88 @@ +/** Support for date/time values. + * + * At the moment this supports dates, but not times. + */ +#ifndef PQXX_H_TIME +#define PQXX_H_TIME + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + +#include "pqxx/internal/concat.hxx" +#include "pqxx/strconv.hxx" + + +#if defined(PQXX_HAVE_YEAR_MONTH_DAY) + +namespace pqxx +{ +using namespace std::literals; + +template<> +struct nullness + : no_null +{}; + + +/// String representation for a Gregorian date in ISO-8601 format. +/** @warning Experimental. There may still be design problems, particularly + * when it comes to BC years. + * + * PostgreSQL supports a choice of date formats, but libpqxx does not. The + * other formats in turn support a choice of "month before day" versus "day + * before month," meaning that it's not necessarily known which format a given + * date is supposed to be. So I repeat: ISO-8601-style format only! + * + * Invalid dates will not convert. This includes February 29 on non-leap + * years, which is why it matters that `year_month_day` represents a + * _Gregorian_ date. + * + * The range of years is limited. At the time of writing, PostgreSQL 14 + * supports years from 4713 BC to 294276 AD inclusive, and C++20 supports + * a range of 32767 BC to 32767 AD inclusive. So in practice, years must fall + * between 4713 BC and 32767 AD, inclusive. + * + * @warning Support for BC (or BCE) years is still experimental. I still need + * confirmation on this issue: it looks as if C++ years are astronomical years, + * which means they have a Year Zero. Regular BC/AD years do not have a year + * zero, so the year 1 AD follows directly after 1 BC. + * + * So, what to our calendars (and to PostgreSQL) is the year "0001 BC" seems to + * count as year "0" in a `std::chrono::year_month_day`. The year 0001 AD is + * still equal to 1 as you'd expect, and all AD years work normally, but all + * years before then are shifted by one. For instance, the year 543 BC would + * be -542 in C++. + */ +template<> struct PQXX_LIBEXPORT string_traits +{ + [[nodiscard]] static zview + to_buf(char *begin, char *end, std::chrono::year_month_day const &value) + { + return generic_to_buf(begin, end, value); + } + + static char * + into_buf(char *begin, char *end, std::chrono::year_month_day const &value); + + [[nodiscard]] static std::chrono::year_month_day + from_string(std::string_view text); + + [[nodiscard]] static std::size_t + size_buffer(std::chrono::year_month_day const &) noexcept + { + static_assert(int{(std::chrono::year::min)()} >= -99999); + static_assert(int{(std::chrono::year::max)()} <= 99999); + return 5 + 1 + 2 + 1 + 2 + std::size(s_bc) + 1; + } + +private: + /// The "BC" suffix for years before 1 AD. + static constexpr std::string_view s_bc{" BC"sv}; +}; +} // namespace pqxx +#endif // PQXX_HAVE_YEAR_MONTH_DAY +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction new file mode 100644 index 000000000..a7ae39d43 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction @@ -0,0 +1,8 @@ +/** pqxx::transaction class. + * + * pqxx::transaction represents a standard database transaction. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/transaction.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction.hxx new file mode 100644 index 000000000..e90917e38 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction.hxx @@ -0,0 +1,108 @@ +/* Definition of the pqxx::transaction class. + * pqxx::transaction represents a standard database transaction. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/transaction instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_TRANSACTION +#define PQXX_H_TRANSACTION + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/dbtransaction.hxx" + +namespace pqxx::internal +{ +/// Helper base class for the @ref transaction class template. +class PQXX_LIBEXPORT basic_transaction : public dbtransaction +{ +protected: + basic_transaction( + connection &c, zview begin_command, std::string_view tname); + basic_transaction(connection &c, zview begin_command, std::string &&tname); + basic_transaction(connection &c, zview begin_command); + + virtual ~basic_transaction() noexcept override = 0; + +private: + virtual void do_commit() override; +}; +} // namespace pqxx::internal + + +namespace pqxx +{ +/** + * @ingroup transactions + */ +//@{ + +/// Standard back-end transaction, templatised on isolation level. +/** This is the type you'll normally want to use to represent a transaction on + * the database. + * + * Usage example: double all wages. + * + * ```cxx + * extern connection C; + * work T(C); + * try + * { + * T.exec0("UPDATE employees SET wage=wage*2"); + * T.commit(); // NOTE: do this inside try block + * } + * catch (exception const &e) + * { + * cerr << e.what() << endl; + * T.abort(); // Usually not needed; same happens when T's life ends. + * } + * ``` + */ +template< + isolation_level ISOLATION = isolation_level::read_committed, + write_policy READWRITE = write_policy::read_write> +class transaction final : public internal::basic_transaction +{ +public: + /// Begin a transaction. + /** + * @param c Connection for this transaction to operate on. + * @param tname Optional name for transaction. Must begin with a letter and + * may contain letters and digits only. + */ + transaction(connection &c, std::string_view tname) : + internal::basic_transaction{ + c, internal::begin_cmd, tname} + {} + + /// Begin a transaction. + /** + * @param c Connection for this transaction to operate on. + * may contain letters and digits only. + */ + explicit transaction(connection &c) : + internal::basic_transaction{ + c, internal::begin_cmd} + {} + + virtual ~transaction() noexcept override { close(); } +}; + + +/// The default transaction type. +using work = transaction<>; + +/// Read-only transaction. +using read_transaction = + transaction; + +//@} +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_base b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_base new file mode 100644 index 000000000..c39219aac --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_base @@ -0,0 +1,9 @@ +/** Base for the transaction classes. + * + * pqxx::transaction_base defines the interface for any abstract class that + * represents a database transaction. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/transaction_base.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_base.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_base.hxx new file mode 100644 index 000000000..4363cc56a --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_base.hxx @@ -0,0 +1,810 @@ +/* Common code and definitions for the transaction classes. + * + * pqxx::transaction_base defines the interface for any abstract class that + * represents a database transaction. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/transaction_base instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_TRANSACTION_BASE +#define PQXX_H_TRANSACTION_BASE + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + +/* End-user programs need not include this file, unless they define their own + * transaction classes. This is not something the typical program should want + * to do. + * + * However, reading this file is worthwhile because it defines the public + * interface for the available transaction classes such as transaction and + * nontransaction. + */ + +#include "pqxx/connection.hxx" +#include "pqxx/internal/concat.hxx" +#include "pqxx/internal/encoding_group.hxx" +#include "pqxx/isolation.hxx" +#include "pqxx/result.hxx" +#include "pqxx/row.hxx" +#include "pqxx/stream_from.hxx" +#include "pqxx/util.hxx" + +namespace pqxx::internal::gate +{ +class transaction_subtransaction; +class transaction_sql_cursor; +class transaction_stream_to; +class transaction_transaction_focus; +} // namespace pqxx::internal::gate + + +namespace pqxx +{ +using namespace std::literals; + + +class transaction_focus; + + +/** + * @defgroup transactions Transaction classes + * + * All database access goes through instances of these classes. + * However, not all implementations of this interface need to provide full + * transactional integrity. + * + * Several implementations of this interface are shipped with libpqxx, + * including the plain transaction class, the entirely unprotected + * nontransaction, and the more cautious robusttransaction. + */ + +/// Interface definition (and common code) for "transaction" classes. +/** + * @ingroup transactions + * + * Abstract base class for all transaction types. + */ +class PQXX_LIBEXPORT PQXX_NOVTABLE transaction_base +{ +public: + transaction_base() = delete; + transaction_base(transaction_base const &) = delete; + transaction_base(transaction_base &&) = delete; + transaction_base &operator=(transaction_base const &) = delete; + transaction_base &operator=(transaction_base &&) = delete; + + virtual ~transaction_base() = 0; + + /// Commit the transaction. + /** Make the effects of this transaction definite. If you destroy a + * transaction without invoking its @ref commit() first, that will implicitly + * abort it. (For the @ref nontransaction class though, "commit" and "abort" + * really don't do anything, hence its name.) + * + * There is, however, a minute risk that you might lose your connection to + * the database at just the wrong moment here. In that case, libpqxx may be + * unable to determine whether the database was able to complete the + * transaction, or had to roll it back. In that scenario, @ref commit() will + * throw an in_doubt_error. There is a different transaction class called + * @ref robusttransaction which takes some special precautions to reduce this + * risk. + */ + void commit(); + + /// Abort the transaction. + /** No special effort is required to call this function; it will be called + * implicitly when the transaction is destructed. + */ + void abort(); + + /** + * @ingroup escaping-functions + * + * Use these when writing SQL queries that incorporate C++ values as SQL + * constants. + * + * The functions you see here are just convenience shortcuts to the same + * functions on the connection object. + */ + //@{ + /// Escape string for use as SQL string literal in this transaction. + template [[nodiscard]] auto esc(ARGS &&...args) const + { + return conn().esc(std::forward(args)...); + } + + /// Escape binary data for use as SQL string literal in this transaction. + /** Raw, binary data is treated differently from regular strings. Binary + * strings are never interpreted as text, so they may safely include byte + * values or byte sequences that don't happen to represent valid characters + * in the character encoding being used. + * + * The binary string does not stop at the first zero byte, as is the case + * with textual strings. Instead, it may contain zero bytes anywhere. If + * it happens to contain bytes that look like quote characters, or other + * things that can disrupt their use in SQL queries, they will be replaced + * with special escape sequences. + */ + template [[nodiscard]] auto esc_raw(ARGS &&...args) const + { + return conn().esc_raw(std::forward(args)...); + } + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string + unesc_raw(zview text) const + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return conn().unesc_raw(text); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard]] std::basic_string unesc_bin(zview text) + { + return conn().unesc_bin(text); + } + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string + unesc_raw(char const *text) const + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return conn().unesc_raw(text); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Unescape binary data, e.g. from a table field or notification payload. + /** Takes a binary string as escaped by PostgreSQL, and returns a restored + * copy of the original binary data. + */ + [[nodiscard]] std::basic_string unesc_bin(char const text[]) + { + return conn().unesc_bin(text); + } + + /// Represent object as SQL string, including quoting & escaping. + /** Nulls are recognized and represented as SQL nulls. */ + template [[nodiscard]] std::string quote(T const &t) const + { + return conn().quote(t); + } + + [[deprecated( + "Use std::basic_string instead of binarystring.")]] std::string + quote(binarystring const &t) const + { + return conn().quote(t.bytes_view()); + } + + /// Binary-escape and quote a binary string for use as an SQL constant. + [[deprecated("Use quote(std::basic_string_view).")]] std::string + quote_raw(unsigned char const bin[], std::size_t len) const + { + return quote(binary_cast(bin, len)); + } + + /// Binary-escape and quote a binary string for use as an SQL constant. + [[deprecated("Use quote(std::basic_string_view).")]] std::string + quote_raw(zview bin) const; + +#if defined(PQXX_HAVE_CONCEPTS) + /// Binary-escape and quote a binary string for use as an SQL constant. + /** For binary data you can also just use @ref quote(data). */ + template + [[nodiscard]] std::string quote_raw(DATA const &data) const + { + return conn().quote_raw(data); + } +#endif + + /// Escape an SQL identifier for use in a query. + [[nodiscard]] std::string quote_name(std::string_view identifier) const + { + return conn().quote_name(identifier); + } + + /// Escape string for literal LIKE match. + [[nodiscard]] std::string + esc_like(std::string_view bin, char escape_char = '\\') const + { + return conn().esc_like(bin, escape_char); + } + //@} + + /** + * @name Command execution + * + * There are many functions for executing (or "performing") a command (or + * "query"). This is the most fundamental thing you can do with the library, + * and you always do it from a transaction class. + * + * Command execution can throw many types of exception, including sql_error, + * broken_connection, and many sql_error subtypes such as + * feature_not_supported or insufficient_privilege. But any exception thrown + * by the C++ standard library may also occur here. All exceptions you will + * see libpqxx throw are derived from std::exception. + * + * One unusual feature in libpqxx is that you can give your query a name or + * description. This does not mean anything to the database, but sometimes + * it can help libpqxx produce more helpful error messages, making problems + * in your code easier to debug. + * + * Many of the execution functions used to accept a `desc` argument, a + * human-readable description of the statement for use in error messages. + * This could make failures easier to debug. Future versions will use + * C++20's `std::source_location` to identify the failing statement. + */ + //@{ + + /// Execute a command. + /** + * @param query Query or command to execute. + * @param desc Optional identifier for query, to help pinpoint SQL errors. + * @return A result set describing the query's or command's result. + */ + [[deprecated("The desc parameter is going away.")]] result + exec(std::string_view query, std::string_view desc); + + /// Execute a command. + /** + * @param query Query or command to execute. + * @return A result set describing the query's or command's result. + */ + result exec(std::string_view query) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return exec(query, std::string_view{}); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Execute a command. + /** + * @param query Query or command to execute. + * @param desc Optional identifier for query, to help pinpoint SQL errors. + * @return A result set describing the query's or command's result. + */ + [[deprecated( + "Pass your query as a std::string_view, not stringstream.")]] result + exec(std::stringstream const &query, std::string_view desc) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return exec(query.str(), desc); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Execute command, which should return zero rows of data. + /** Works like @ref exec, but fails if the result contains data. It still + * returns a result, however, which may contain useful metadata. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + [[deprecated("The desc parameter is going away.")]] result + exec0(zview query, std::string_view desc) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return exec_n(0, query, desc); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Execute command, which should return zero rows of data. + /** Works like @ref exec, but fails if the result contains data. It still + * returns a result, however, which may contain useful metadata. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + result exec0(zview query) { return exec_n(0, query); } + + /// Execute command returning a single row of data. + /** Works like @ref exec, but requires the result to contain exactly one row. + * The row can be addressed directly, without the need to find the first row + * in a result set. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + [[deprecated("The desc parameter is going away.")]] row + exec1(zview query, std::string_view desc) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return exec_n(1, query, desc).front(); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Execute command returning a single row of data. + /** Works like @ref exec, but requires the result to contain exactly one row. + * The row can be addressed directly, without the need to find the first row + * in a result set. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + row exec1(zview query) { return exec_n(1, query).front(); } + + /// Execute command, expect given number of rows. + /** Works like @ref exec, but checks that the result has exactly the expected + * number of rows. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + [[deprecated("The desc parameter is going away.")]] result + exec_n(result::size_type rows, zview query, std::string_view desc); + + /// Execute command, expect given number of rows. + /** Works like @ref exec, but checks that the result has exactly the expected + * number of rows. + * + * @throw unexpected_rows If the query returned the wrong number of rows. + */ + result exec_n(result::size_type rows, zview query) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + return exec_n(rows, query, std::string_view{}); +#include "pqxx/internal/ignore-deprecated-post.hxx" + } + + /// Perform query, expecting exactly 1 row with 1 field, and convert it. + /** This is convenience shorthand for querying exactly one value from the + * database. It returns that value, converted to the type you specify. + */ + template + [[deprecated("The desc parameter is going away.")]] TYPE + query_value(zview query, std::string_view desc) + { +#include "pqxx/internal/ignore-deprecated-pre.hxx" + row const r{exec1(query, desc)}; +#include "pqxx/internal/ignore-deprecated-post.hxx" + if (std::size(r) != 1) + throw usage_error{internal::concat( + "Queried single value from result with ", std::size(r), " columns.")}; + return r[0].as(); + } + + /// Perform query, expecting exactly 1 row with 1 field, and convert it. + /** This is convenience shorthand for querying exactly one value from the + * database. It returns that value, converted to the type you specify. + */ + template TYPE query_value(zview query) + { + row const r{exec1(query)}; + if (std::size(r) != 1) + throw usage_error{internal::concat( + "Queried single value from result with ", std::size(r), " columns.")}; + return r[0].as(); + } + + /// Execute a query, and loop over the results row by row. + /** Converts the rows to `std::tuple`, of the column types you specify. + * + * Use this with a range-based "for" loop. It executes the query, and + * directly maps the resulting rows onto a `std::tuple` of the types you + * specify. It starts before all the data from the server is in, so if your + * network connection to the server breaks while you're iterating, you'll get + * an exception partway through. + * + * The stream lives entirely within the lifetime of the transaction. Make + * sure you destroy the stream before you destroy the transaction. Either + * iterate the stream all the way to the end, or destroy first the stream + * and then the transaction without touching either in any other way. Until + * the stream has finished, the transaction is in a special state where it + * cannot execute queries. + * + * As a special case, tuple may contain `std::string_view` fields, but the + * strings to which they point will only remain valid until you extract the + * next row. After that, the memory holding the string may be overwritten or + * deallocated. + * + * If any of the columns can be null, and the C++ type to which it translates + * does not have a null value, wrap the type in `std::optional` (or if + * you prefer, `std::shared_ptr` or `std::unique_ptr)`. These templates + * do recognise null values, and libpqxx will know how to convert to them. + * + * The connection is in a special state until the iteration finishes. So if + * it does not finish due to a `break` or a `return` or an exception, then + * the entire connection becomes effectively unusable. + * + * Querying in this way is faster than the `exec()` methods for larger + * results (but slower for small ones). You can start processing rows before + * the full result is in. Also, `stream()` scales better in terms of memory + * usage. Where @ref exec() reads the entire result into memory at once, + * `stream()` will read and process one row at at a time. + * + * Your query executes as part of a COPY command, not as a stand-alone query, + * so there are limitations to what you can do in the query. It can be + * either a SELECT or VALUES query; or an INSERT, UPDATE, or DELETE with a + * RETURNING clause. See the documentation for PostgreSQL's COPY command for + * the details: + * + * https://www.postgresql.org/docs/current/sql-copy.html + * + * Iterating in this way does require each of the field types you pass to be + * default-constructible, copy-constructible, and assignable. These + * requirements may be loosened once libpqxx moves on to C++20. + */ + template + [[nodiscard]] auto stream(std::string_view query) & + { + // Tricky: std::make_unique() supports constructors but not RVO functions. + return pqxx::internal::owning_stream_input_iteration{ + std::unique_ptr{ + new stream_from{stream_from::query(*this, query)}}}; + } + + // C++20: Concept like std::invocable, but without specifying param types. + /// Perform a streaming query, and for each result row, call `func`. + /** Here, `func` can be a function, a `std::function`, a lambda, or an + * object that supports the function call operator. Of course `func` must + * have an unambiguous signature; it can't be overloaded or generic. + * + * The `for_each` function executes `query` in a stream using + * @ref pqxx::stream_from. Every time a row of data comes in from the + * server, it converts the row's fields to the types of `func`'s respective + * parameters, and calls `func` with those values. + * + * This will not work for all queries, but straightforward `SELECT` and + * `UPDATE ... RETURNING` queries should work. Consult the documentation for + * @ref pqxx::stream_from and PostgreSQL's underlying `COPY` command for the + * full details. + * + * Streaming a query like this is likely to be slower than the @ref exec() + * functions for small result sets, but faster for large result sets. So if + * performance matters, you'll want to use `for_each` if you query large + * amounts of data, but not if you do lots of queries with small outputs. + */ + template + inline auto for_each(std::string_view query, CALLABLE &&func) + { + using param_types = + pqxx::internal::strip_types_t>; + param_types const *const sample{nullptr}; + auto data_stream{stream_like(query, sample)}; + for (auto const &fields : data_stream) std::apply(func, fields); + } + + /** + * @name Parameterized statements + * + * You'll often need parameters in the queries you execute: "select the + * car with this licence plate." If the parameter is a string, you need to + * quote it and escape any special characters inside it, or it may become a + * target for an SQL injection attack. If it's an integer (for example), + * you need to convert it to a string, but in the database's format, without + * locale-specific niceties like "," separators between the thousands. + * + * Parameterised statements are an easier and safer way to do this. They're + * like prepared statements, but for a single use. You don't need to name + * them, and you don't need to prepare them first. + * + * Your query will include placeholders like `$1` and `$2` etc. in the places + * where you want the arguments to go. Then, you pass the argument values + * and the actual query is constructed for you. + * + * Pass the exact right number of parameters, and in the right order. The + * parameters in the query don't have to be neatly ordered from `$1` to + * `$2` to `$3` - but you must pass the argument for `$1` first, the one + * for `$2` second, etc. + * + * @warning Beware of "nul" bytes. Any string you pass as a parameter will + * end at the first char with value zero. If you pass a string that contains + * a zero byte, the last byte in the value will be the one just before the + * zero. + */ + //@{ + /// Execute an SQL statement with parameters. + template result exec_params(zview query, Args &&...args) + { + params pp(args...); + return internal_exec_params(query, pp.make_c_params()); + } + + // Execute parameterised statement, expect a single-row result. + /** @throw unexpected_rows if the result does not consist of exactly one row. + */ + template row exec_params1(zview query, Args &&...args) + { + return exec_params_n(1, query, std::forward(args)...).front(); + } + + // Execute parameterised statement, expect a result with zero rows. + /** @throw unexpected_rows if the result contains rows. + */ + template result exec_params0(zview query, Args &&...args) + { + return exec_params_n(0, query, std::forward(args)...); + } + + // Execute parameterised statement, expect exactly a given number of rows. + /** @throw unexpected_rows if the result contains the wrong number of rows. + */ + template + result exec_params_n(std::size_t rows, zview query, Args &&...args) + { + auto const r{exec_params(query, std::forward(args)...)}; + check_rowcount_params(rows, std::size(r)); + return r; + } + //@} + + /** + * @name Prepared statements + * + * These are very similar to parameterised statements. The difference is + * that you prepare them in advance, giving them identifying names. You can + * then call them by these names, passing in the argument values appropriate + * for that call. + * + * You prepare a statement on the connection, using + * @ref pqxx::connection::prepare(). But you then call the statement in a + * transaction, using the functions you see here. + * + * Never try to prepare, execute, or unprepare a prepared statement manually + * using direct SQL queries when you also use the libpqxx equivalents. For + * any given statement, either prepare, manage, and execute it through the + * dedicated libpqxx functions; or do it all directly in SQL. Don't mix the + * two, or the code may get confused. + * + * See \ref prepared for a full discussion. + * + * @warning Beware of "nul" bytes. Any string you pass as a parameter will + * end at the first char with value zero. If you pass a string that contains + * a zero byte, the last byte in the value will be the one just before the + * zero. If you need a zero byte, you're dealing with binary strings, not + * regular strings. Represent binary strings on the SQL side as `BYTEA` + * (or as large objects). On the C++ side, use types like + * `std::basic_string` or `std::basic_string_view` + * or (in C++20) `std::vector`. Also, consider large objects on + * the SQL side and @ref blob on the C++ side. + */ + //@{ + + /// Execute a prepared statement, with optional arguments. + template + result exec_prepared(zview statement, Args &&...args) + { + params pp(args...); + return internal_exec_prepared(statement, pp.make_c_params()); + } + + /// Execute a prepared statement, and expect a single-row result. + /** @throw pqxx::unexpected_rows if the result was not exactly 1 row. + */ + template + row exec_prepared1(zview statement, Args &&...args) + { + return exec_prepared_n(1, statement, std::forward(args)...).front(); + } + + /// Execute a prepared statement, and expect a result with zero rows. + /** @throw pqxx::unexpected_rows if the result contained rows. + */ + template + result exec_prepared0(zview statement, Args &&...args) + { + return exec_prepared_n(0, statement, std::forward(args)...); + } + + /// Execute a prepared statement, expect a result with given number of rows. + /** @throw pqxx::unexpected_rows if the result did not contain exactly the + * given number of rows. + */ + template + result + exec_prepared_n(result::size_type rows, zview statement, Args &&...args) + { + auto const r{exec_prepared(statement, std::forward(args)...)}; + check_rowcount_prepared(statement, rows, std::size(r)); + return r; + } + + //@} + + /** + * @name Error/warning output + */ + //@{ + /// Have connection process a warning message. + void process_notice(char const msg[]) const { m_conn.process_notice(msg); } + /// Have connection process a warning message. + void process_notice(zview msg) const { m_conn.process_notice(msg); } + //@} + + /// The connection in which this transaction lives. + [[nodiscard]] constexpr connection &conn() const noexcept { return m_conn; } + + /// Set session variable using SQL "SET" command. + /** @deprecated To set a transaction-local variable, execute an SQL `SET` + * command. To set a session variable, use the connection's + * @ref set_session_var function. + * + * @warning When setting a string value, you must make sure that the string + * is "safe." If you call @ref quote() on the string, it will return a + * safely escaped and quoted version for use as an SQL literal. + * + * @warning This function executes SQL. Do not try to set or get variables + * while a pipeline or table stream is active. + * + * @param var The variable to set. + * @param value The new value to store in the variable. This can be any SQL + * expression. + */ + [[deprecated( + "Set transaction-local variables using SQL SET statements.")]] void + set_variable(std::string_view var, std::string_view value); + + /// Read session variable using SQL "SHOW" command. + /** @warning This executes SQL. Do not try to set or get variables while a + * pipeline or table stream is active. + */ + [[deprecated("Read variables using SQL SHOW statements.")]] std::string + get_variable(std::string_view); + + // C++20: constexpr. + /// Transaction name, if you passed one to the constructor; or empty string. + [[nodiscard]] std::string_view name() const &noexcept { return m_name; } + +protected: + /// Create a transaction (to be called by implementation classes only). + /** The name, if nonempty, must begin with a letter and may contain letters + * and digits only. + */ + transaction_base( + connection &c, std::string_view tname, + std::shared_ptr rollback_cmd) : + m_conn{c}, m_name{tname}, m_rollback_cmd{rollback_cmd} + {} + + /// Create a transaction (to be called by implementation classes only). + /** Its rollback command will be "ROLLBACK". + * + * The name, if nonempty, must begin with a letter and may contain letters + * and digits only. + */ + transaction_base(connection &c, std::string_view tname); + + /// Create a transaction (to be called by implementation classes only). + explicit transaction_base(connection &c); + + /// Register this transaction with the connection. + void register_transaction(); + + /// End transaction. To be called by implementing class' destructor. + void close() noexcept; + + /// To be implemented by derived implementation class: commit transaction. + virtual void do_commit() = 0; + + /// Transaction type-specific way of aborting a transaction. + /** @warning This will become "final", since this function can be called + * from the implementing class destructor. + */ + virtual void do_abort(); + + /// Set the rollback command. + void set_rollback_cmd(std::shared_ptr cmd) + { + m_rollback_cmd = cmd; + } + + /// Execute query on connection directly. + result direct_exec(std::string_view, std::string_view desc = ""sv); + result + direct_exec(std::shared_ptr, std::string_view desc = ""sv); + +private: + enum class status + { + active, + aborted, + committed, + in_doubt + }; + + PQXX_PRIVATE void check_pending_error(); + + result + internal_exec_prepared(zview statement, internal::c_params const &args); + + result internal_exec_params(zview query, internal::c_params const &args); + + /// Throw unexpected_rows if prepared statement returned wrong no. of rows. + void check_rowcount_prepared( + zview statement, result::size_type expected_rows, + result::size_type actual_rows); + + /// Throw unexpected_rows if wrong row count from parameterised statement. + void + check_rowcount_params(std::size_t expected_rows, std::size_t actual_rows); + + /// Describe this transaction to humans, e.g. "transaction 'foo'". + [[nodiscard]] std::string description() const; + + friend class pqxx::internal::gate::transaction_transaction_focus; + PQXX_PRIVATE void register_focus(transaction_focus *); + PQXX_PRIVATE void unregister_focus(transaction_focus *) noexcept; + PQXX_PRIVATE void register_pending_error(zview) noexcept; + PQXX_PRIVATE void register_pending_error(std::string &&) noexcept; + + /// Like @ref stream(), but takes a tuple rather than a parameter pack. + template + auto stream_like(std::string_view query, std::tuple const *) + { + return stream(query); + } + + connection &m_conn; + + /// Current "focus": a pipeline, a nested transaction, a stream... + /** This pointer is used for only one purpose: sanity checks against mistakes + * such as opening one while another is still active. + */ + transaction_focus const *m_focus = nullptr; + + status m_status = status::active; + bool m_registered = false; + std::string m_name; + std::string m_pending_error; + + /// SQL command for aborting this type of transaction. + std::shared_ptr m_rollback_cmd; + + static constexpr std::string_view s_type_name{"transaction"sv}; +}; + + +// C++20: Can borrowed_range help? +/// Forbidden specialisation: underlying buffer immediately goes out of scope. +template<> +std::string_view transaction_base::query_value( + zview query, std::string_view desc) = delete; +/// Forbidden specialisation: underlying buffer immediately goes out of scope. +template<> +zview transaction_base::query_value( + zview query, std::string_view desc) = delete; + +} // namespace pqxx + + +namespace pqxx::internal +{ +/// The SQL command for starting a given type of transaction. +template +extern const zview begin_cmd; + +// These are not static members, so "constexpr" does not imply "inline". +template<> +inline constexpr zview begin_cmd{ + "BEGIN"_zv}; +template<> +inline constexpr zview begin_cmd{ + "BEGIN READ ONLY"_zv}; +template<> +inline constexpr zview begin_cmd{ + "BEGIN ISOLATION LEVEL REPEATABLE READ"_zv}; +template<> +inline constexpr zview begin_cmd{ + "BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY"_zv}; +template<> +inline constexpr zview begin_cmd{ + "BEGIN ISOLATION LEVEL SERIALIZABLE"_zv}; +template<> +inline constexpr zview begin_cmd{ + "BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY"_zv}; +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_focus b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_focus new file mode 100644 index 000000000..fe78a9bcc --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_focus @@ -0,0 +1,7 @@ +/** + * Transaction focus: types which monopolise a transaction's attention. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/types.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_focus.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_focus.hxx new file mode 100644 index 000000000..0707e3cc4 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transaction_focus.hxx @@ -0,0 +1,89 @@ +/** Transaction focus: types which monopolise a transaction's attention. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_TRANSACTION_FOCUS +#define PQXX_H_TRANSACTION_FOCUS + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include "pqxx/util.hxx" + +namespace pqxx +{ +/// Base class for things that monopolise a transaction's attention. +/** You probably won't need to use this class. But it can be useful to _know_ + * that a given libpqxx class is derived from it. + * + * Pipelines, SQL statements, and data streams are examples of classes derived + * from `transaction_focus`. For any given transaction, only one object of + * such a class can be active at any given time. + */ +class PQXX_LIBEXPORT transaction_focus +{ +public: + transaction_focus( + transaction_base &t, std::string_view cname, std::string_view oname) : + m_trans{t}, m_classname{cname}, m_name{oname} + {} + + transaction_focus( + transaction_base &t, std::string_view cname, std::string &&oname) : + m_trans{t}, m_classname{cname}, m_name{std::move(oname)} + {} + + transaction_focus(transaction_base &t, std::string_view cname) : + m_trans{t}, m_classname{cname} + {} + + transaction_focus() = delete; + transaction_focus(transaction_focus const &) = delete; + transaction_focus &operator=(transaction_focus const &) = delete; + + /// Class name, for human consumption. + [[nodiscard]] constexpr std::string_view classname() const noexcept + { + return m_classname; + } + + /// Name for this object, if the caller passed one; empty string otherwise. + [[nodiscard]] std::string_view name() const &noexcept { return m_name; } + + [[nodiscard]] std::string description() const + { + return pqxx::internal::describe_object(m_classname, m_name); + } + + /// Can't move a transaction_focus. + /** Moving the transaction_focus would break the transaction's reference back + * to the object. + */ + transaction_focus(transaction_focus &&) = delete; + + /// Can't move a transaction_focus. + /** Moving the transaction_focus would break the transaction's reference back + * to the object. + */ + transaction_focus &operator=(transaction_focus &&) = delete; + +protected: + void register_me(); + void unregister_me() noexcept; + void reg_pending_error(std::string const &) noexcept; + bool registered() const noexcept { return m_registered; } + + transaction_base &m_trans; + +private: + bool m_registered = false; + std::string_view m_classname; + std::string m_name; +}; +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transactor b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transactor new file mode 100644 index 000000000..29d1b9640 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transactor @@ -0,0 +1,8 @@ +/** pqxx::transactor class. + * + * pqxx::transactor is a framework-style wrapper for safe transactions. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/transactor.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transactor.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transactor.hxx new file mode 100644 index 000000000..eefd04ba1 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/transactor.hxx @@ -0,0 +1,147 @@ +/* Transactor framework, a wrapper for safely retryable transactions. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/transactor instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_TRANSACTOR +#define PQXX_H_TRANSACTOR + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include + +#include "pqxx/connection.hxx" +#include "pqxx/transaction.hxx" + +namespace pqxx +{ +/** + * @defgroup transactor Transactor framework + * + * Sometimes a transaction can fail for completely transient reasons, such as a + * conflict with another transaction in SERIALIZABLE isolation. The right way + * to handle those failures is often just to re-run the transaction from + * scratch. + * + * For example, your REST API might be handling each HTTP request in its own + * database transaction, and if this kind of transient failure happens, you + * simply want to "replay" the whole request, in a fresh transaction. + * + * You won't necessarily want to execute the exact same SQL commands with the + * exact same data. Some of your SQL statements may depend on state that can + * vary between retries. Data in the database may already have changed, for + * instance. So instead of dumbly replaying the SQL, you re-run the same + * application code that produced those SQL commands, from the start. + * + * The transactor framework makes it a little easier for you to do this safely, + * and avoid typical pitfalls. You encapsulate the work that you want to do + * into a callable that you pass to the @ref perform function. + * + * Here's how it works. You write your transaction code as a lambda or + * function, which creates its own transaction object, does its work, and + * commits at the end. You pass that callback to @ref pqxx::perform, which + * runs it for you. + * + * If there's a failure inside your callback, there will be an exception. Your + * transaction object goes out of scope and gets destroyed, so that it aborts + * implicitly. Seeing this, @ref perform tries running your callback again. It + * stops doing that when the callback succeeds, or when it has failed too many + * times, or when there's an error that leaves the database in an unknown + * state, such as a lost connection just while we're waiting for the database + * to confirm a commit. It all depends on the type of exception. + * + * The callback takes no arguments. If you're using lambdas, the easy way to + * pass arguments is for the lambda to "capture" them from your variables. Or, + * if you're using functions, you may want to use `std::bind`. + * + * Once your callback succeeds, it can return a result, and @ref perform will + * return that result back to you. + */ +//@{ + +/// Simple way to execute a transaction with automatic retry. +/** + * Executes your transaction code as a callback. Repeats it until it completes + * normally, or it throws an error other than the few libpqxx-generated + * exceptions that the framework understands, or after a given number of failed + * attempts, or if the transaction ends in an "in-doubt" state. + * + * (An in-doubt state is one where libpqxx cannot determine whether the server + * finally committed a transaction or not. This can happen if the network + * connection to the server is lost just while we're waiting for its reply to + * a "commit" statement. The server may have completed the commit, or not, but + * it can't tell you because there's no longer a connection. + * + * Using this still takes a bit of care. If your callback makes use of data + * from the database, you'll probably have to query that data within your + * callback. If the attempt to perform your callback fails, and the framework + * tries again, you'll be in a new transaction and the data in the database may + * have changed under your feet. + * + * Also be careful about changing variables or data structures from within + * your callback. The run may still fail, and perhaps get run again. The + * ideal way to do it (in most cases) is to return your result from your + * callback, and change your program's data state only after @ref perform + * completes successfully. + * + * @param callback Transaction code that can be called with no arguments. + * @param attempts Maximum number of times to attempt performing callback. + * Must be greater than zero. + * @return Whatever your callback returns. + */ +template +inline auto perform(TRANSACTION_CALLBACK &&callback, int attempts = 3) + -> std::invoke_result_t +{ + if (attempts <= 0) + throw std::invalid_argument{ + "Zero or negative number of attempts passed to pqxx::perform()."}; + + for (; attempts > 0; --attempts) + { + try + { + return std::invoke(callback); + } + catch (in_doubt_error const &) + { + // Not sure whether transaction went through or not. The last thing in + // the world that we should do now is try again! + throw; + } + catch (statement_completion_unknown const &) + { + // Not sure whether our last statement succeeded. Don't risk running it + // again. + throw; + } + catch (broken_connection const &) + { + // Connection failed. May be worth retrying, if the transactor opens its + // own connection. + if (attempts <= 1) + throw; + continue; + } + catch (transaction_rollback const &) + { + // Some error that may well be transient, such as serialization failure + // or deadlock. Worth retrying. + if (attempts <= 1) + throw; + continue; + } + } + throw pqxx::internal_error{"No outcome reached on perform()."}; +} +} // namespace pqxx +//@} +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/types b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/types new file mode 100644 index 000000000..23a5caae1 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/types @@ -0,0 +1,7 @@ +/** + * Basic typedefs and forward declarations. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/types.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/types.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/types.hxx new file mode 100644 index 000000000..f95b598f8 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/types.hxx @@ -0,0 +1,173 @@ +/* Basic type aliases and forward declarations. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_TYPES +#define PQXX_H_TYPES + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include + +#if defined(PQXX_HAVE_CONCEPTS) && __has_include() +# include +#endif + + +namespace pqxx +{ +/// Number of rows in a result set. +using result_size_type = int; + +/// Difference between result sizes. +using result_difference_type = int; + +/// Number of fields in a row of database data. +using row_size_type = int; + +/// Difference between row sizes. +using row_difference_type = int; + +/// Number of bytes in a field of database data. +using field_size_type = std::size_t; + +/// Number of bytes in a large object. +using large_object_size_type = int64_t; + + +// Forward declarations, to help break compilation dependencies. +// These won't necessarily include all classes in libpqxx. +class binarystring; +class connection; +class const_result_iterator; +class const_reverse_result_iterator; +class const_reverse_row_iterator; +class const_row_iterator; +class dbtransaction; +class field; +class largeobjectaccess; +class notification_receiver; +struct range_error; +class result; +class row; +class stream_from; +class transaction_base; + +/// Marker for @ref stream_from constructors: "stream from table." +/** @deprecated Use @ref stream_from::table() instead. + */ +struct from_table_t +{}; + +/// Marker for @ref stream_from constructors: "stream from query." +/** @deprecated Use @ref stream_from::query() instead. + */ +struct from_query_t +{}; + + +/// Format code: is data text or binary? +/** Binary-compatible with libpq's format codes. + */ +enum class format : int +{ + text = 0, + binary = 1, +}; + + +/// Remove any constness, volatile, and reference-ness from a type. +/** @deprecated In C++20 we'll replace this with std::remove_cvref. + */ +template +using strip_t = std::remove_cv_t>; + + +#if defined(PQXX_HAVE_CONCEPTS) +/// The type of a container's elements. +/** At the time of writing there's a similar thing in `std::experimental`, + * which we may or may not end up using for this. + */ +template +using value_type = strip_t()))>; +#else // PQXX_HAVE_CONCEPTS +/// The type of a container's elements. +/** At the time of writing there's a similar thing in `std::experimental`, + * which we may or may not end up using for this. + */ +template +using value_type = strip_t()))>; +#endif // PQXX_HAVE_CONCEPTS + + +#if defined(PQXX_HAVE_CONCEPTS) +/// Concept: Any type that we can read as a string of `char`. +template +concept char_string = std::ranges::contiguous_range and + std::same_as < strip_t>, +char > ; + +/// Concept: Anything we can iterate to get things we can read as strings. +template +concept char_strings = + std::ranges::range and char_string>>; + +/// Concept: Anything we might want to treat as binary data. +template +concept potential_binary = std::ranges::contiguous_range and + (sizeof(value_type) == 1); +#endif // PQXX_HAVE_CONCEPTS + + +// C++20: Retire these compatibility definitions. +#if defined(PQXX_HAVE_CONCEPTS) + +/// Template argument type for a range. +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_RANGE_ARG std::ranges::range + +/// Template argument type for @ref char_string. +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_CHAR_STRING_ARG pqxx::char_string + +/// Template argument type for @ref char_strings +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_CHAR_STRINGS_ARG pqxx::char_strings + +#else // PQXX_HAVE_CONCEPTS + +/// Template argument type for a range. +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_RANGE_ARG typename + +/// Template argument type for @ref char_string. +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_CHAR_STRING_ARG typename + +/// Template argument type for @ref char_strings +/** This is a concept, so only available in C++20 or better. In pre-C++20 + * environments it's just an alias for @ref typename. + */ +# define PQXX_CHAR_STRINGS_ARG typename + +#endif // PQXX_HAVE_CONCEPTS +} // namespace pqxx +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/util b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/util new file mode 100644 index 000000000..6d85ab611 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/util @@ -0,0 +1,6 @@ +/** Various utility definitions for libpqxx. + */ +// Actual definitions in .hxx file so editors and such recognize file type +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/util.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/util.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/util.hxx new file mode 100644 index 000000000..4aa5ecf57 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/util.hxx @@ -0,0 +1,521 @@ +/* Various utility definitions for libpqxx. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/util instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_UTIL +#define PQXX_H_UTIL + +#if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __has_include() +# include +#endif + +#include "pqxx/except.hxx" +#include "pqxx/internal/encodings.hxx" +#include "pqxx/types.hxx" +#include "pqxx/version.hxx" + + +/// The home of all libpqxx classes, functions, templates, etc. +namespace pqxx +{} + +#include + + +/// Internal items for libpqxx' own use. Do not use these yourself. +namespace pqxx::internal +{ + +// C++20: Retire wrapper. +/// Same as `std::cmp_less`, or a workaround where that's not available. +template +inline constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept +{ +#if defined(PQXX_HAVE_CMP) + return std::cmp_less(lhs, rhs); +#else + // We need a variable just because lgtm.com gives off a false positive + // warning when we compare the values directly. It considers that a + // "self-comparison." + constexpr bool left_signed{std::is_signed_v}; + if constexpr (left_signed == std::is_signed_v) + return lhs < rhs; + else if constexpr (std::is_signed_v) + return (lhs <= 0) ? true : (std::make_unsigned_t(lhs) < rhs); + else + return (rhs <= 0) ? false : (lhs < std::make_unsigned_t(rhs)); +#endif +} + + +// C++20: Retire wrapper. +/// C++20 std::cmp_greater, or workaround if not available. +template +inline constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept +{ +#if defined(PQXX_HAVE_CMP) + return std::cmp_greater(lhs, rhs); +#else + return cmp_less(rhs, lhs); +#endif +} + + +// C++20: Retire wrapper. +/// C++20 std::cmp_less_equal, or workaround if not available. +template +inline constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept +{ +#if defined(PQXX_HAVE_CMP) + return std::cmp_less_equal(lhs, rhs); +#else + return not cmp_less(rhs, lhs); +#endif +} + + +// C++20: Retire wrapper. +/// C++20 std::cmp_greater_equal, or workaround if not available. +template +inline constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept +{ +#if defined(PQXX_HAVE_CMP) + return std::cmp_greater_equal(lhs, rhs); +#else + return not cmp_less(lhs, rhs); +#endif +} + + +/// Efficiently concatenate two strings. +/** This is a special case of concatenate(), needed because dependency + * management does not let us use that function here. + */ +[[nodiscard]] inline std::string cat2(std::string_view x, std::string_view y) +{ + std::string buf; + auto const xs{std::size(x)}, ys{std::size(y)}; + buf.resize(xs + ys); + x.copy(std::data(buf), xs); + y.copy(std::data(buf) + xs, ys); + return buf; +} +} // namespace pqxx::internal + + +namespace pqxx +{ +using namespace std::literals; + +/// Suppress compiler warning about an unused item. +template inline constexpr void ignore_unused(T &&...) noexcept +{} + + +/// Cast a numeric value to another type, or throw if it underflows/overflows. +/** Both types must be arithmetic types, and they must either be both integral + * or both floating-point types. + */ +template +inline TO check_cast(FROM value, std::string_view description) +{ + static_assert(std::is_arithmetic_v); + static_assert(std::is_arithmetic_v); + static_assert(std::is_integral_v == std::is_integral_v); + + // The rest of this code won't quite work for bool, but bool is trivially + // convertible to other arithmetic types as far as I can see. + if constexpr (std::is_same_v) + return static_cast(value); + + // Depending on our "if constexpr" conditions, this parameter may not be + // needed. Some compilers will warn. + ignore_unused(description); + + using from_limits = std::numeric_limits; + using to_limits = std::numeric_limits; + if constexpr (std::is_signed_v) + { + if constexpr (std::is_signed_v) + { + if (value < to_limits::lowest()) + throw range_error{internal::cat2("Cast underflow: "sv, description)}; + } + else + { + // FROM is signed, but TO is not. Treat this as a special case, because + // there may not be a good broader type in which the compiler can even + // perform our check. + if (value < 0) + throw range_error{internal::cat2( + "Casting negative value to unsigned type: "sv, description)}; + } + } + else + { + // No need to check: the value is unsigned so can't fall below the range + // of the TO type. + } + + if constexpr (std::is_integral_v) + { + using unsigned_from = std::make_unsigned_t; + using unsigned_to = std::make_unsigned_t; + constexpr auto from_max{static_cast((from_limits::max)())}; + constexpr auto to_max{static_cast((to_limits::max)())}; + if constexpr (from_max > to_max) + { + if (internal::cmp_greater(value, to_max)) + throw range_error{internal::cat2("Cast overflow: "sv, description)}; + } + } + else if constexpr ((from_limits::max)() > (to_limits::max)()) + { + if (value > (to_limits::max)()) + throw range_error{internal::cat2("Cast overflow: ", description)}; + } + + return static_cast(value); +} + + +/** Check library version at link time. + * + * Ensures a failure when linking an application against a radically + * different libpqxx version than the one against which it was compiled. + * + * Sometimes application builds fail in unclear ways because they compile + * using headers from libpqxx version X, but then link against libpqxx + * binary version Y. A typical scenario would be one where you're building + * against a libpqxx which you have built yourself, but a different version + * is installed on the system. + * + * The check_library_version template is declared for any library version, + * but only actually defined for the version of the libpqxx binary against + * which the code is linked. + * + * If the library binary is a different version than the one declared in + * these headers, then this call will fail to link: there will be no + * definition for the function with these exact template parameter values. + * There will be a definition, but the version in the parameter values will + * be different. + */ +inline PQXX_PRIVATE void check_version() noexcept +{ + // There is no particular reason to do this here in @ref connection, except + // to ensure that every meaningful libpqxx client will execute it. The call + // must be in the execution path somewhere or the compiler won't try to link + // it. We can't use it to initialise a global or class-static variable, + // because a smart compiler might resolve it at compile time. + // + // On the other hand, we don't want to make a useless function call too + // often for performance reasons. A local static variable is initialised + // only on the definition's first execution. Compilers will be well + // optimised for this behaviour, so there's a minimal one-time cost. + static auto const version_ok{internal::PQXX_VERSION_CHECK()}; + ignore_unused(version_ok); +} + + +/// Descriptor of library's thread-safety model. +/** This describes what the library knows about various risks to thread-safety. + */ +struct PQXX_LIBEXPORT thread_safety_model +{ + /// Is the underlying libpq build thread-safe? + bool safe_libpq = false; + + /// Is Kerberos thread-safe? + /** @warning Is currently always `false`. + * + * If your application uses Kerberos, all accesses to libpqxx or Kerberos + * must be serialized. Confine their use to a single thread, or protect it + * with a global lock. + */ + bool safe_kerberos = false; + + /// A human-readable description of any thread-safety issues. + std::string description; +}; + + +/// Describe thread safety available in this build. +[[nodiscard]] PQXX_LIBEXPORT thread_safety_model describe_thread_safety(); + + +#if defined(PQXX_HAVE_CONCEPTS) +# define PQXX_POTENTIAL_BINARY_ARG pqxx::potential_binary +#else +# define PQXX_POTENTIAL_BINARY_ARG typename +#endif + + +/// Cast binary data to a type that libpqxx will recognise as binary. +/** There are many different formats for storing binary data in memory. You + * may have yours as a `std::string`, or a `std::vector`, or one of + * many other types. + * + * But for libpqxx to recognise your data as binary, it needs to be a + * `std::basic_string`, or a `std::basic_string_view`; + * or in C++20 or better, any contiguous block of `std::byte`. + * + * Use `binary_cast` as a convenience helper to cast your data as a + * `std::basic_string_view`. + * + * @warning There are two things you should be aware of! First, the data must + * be contiguous in memory. In C++20 the compiler will enforce this, but in + * C++17 it's your own problem. Second, you must keep the object where you + * store the actual data alive for as long as you might use this function's + * return value. + */ +template +std::basic_string_view binary_cast(TYPE const &data) +{ + static_assert(sizeof(value_type) == 1); + return { + reinterpret_cast( + const_cast const *>( + std::data(data))), + std::size(data)}; +} + + +#if defined(PQXX_HAVE_CONCEPTS) +template +concept char_sized = (sizeof(CHAR) == 1); +# define PQXX_CHAR_SIZED_ARG char_sized +#else +# define PQXX_CHAR_SIZED_ARG typename +#endif + +/// Construct a type that libpqxx will recognise as binary. +/** Takes a data pointer and a size, without being too strict about their + * types, and constructs a `std::basic_string_view` pointing to + * the same data. + * + * This makes it a little easier to turn binary data, in whatever form you + * happen to have it, into binary data as libpqxx understands it. + */ +template +std::basic_string_view binary_cast(CHAR const *data, SIZE size) +{ + static_assert(sizeof(CHAR) == 1); + return { + reinterpret_cast(data), + check_cast(size, "binary data size")}; +} + + +/// The "null" oid. +constexpr oid oid_none{0}; +} // namespace pqxx + + +/// Private namespace for libpqxx's internal use; do not access. +/** This namespace hides definitions internal to libpqxx. These are not + * supposed to be used by client programs, and they may change at any time + * without notice. + * + * Conversely, if you find something in this namespace tremendously useful, by + * all means do lodge a request for its publication. + * + * @warning Here be dragons! + */ +namespace pqxx::internal +{ +using namespace std::literals; + + +/// A safer and more generic replacement for `std::isdigit`. +/** Turns out `std::isdigit` isn't as easy to use as it sounds. It takes an + * `int`, but requires it to be nonnegative. Which means it's an outright + * liability on systems where `char` is signed. + */ +template inline constexpr bool is_digit(CHAR c) noexcept +{ + return (c >= '0') and (c <= '9'); +} + + +/// Describe an object for humans, based on class name and optional name. +/** Interprets an empty name as "no name given." + */ +[[nodiscard]] std::string +describe_object(std::string_view class_name, std::string_view name); + + +/// Check validity of registering a new "guest" in a "host." +/** The host might be e.g. a connection, and the guest a transaction. The + * host can only have one guest at a time, so it is an error to register a new + * guest while the host already has a guest. + * + * If the new registration is an error, this function throws a descriptive + * exception. + * + * Pass the old guest (if any) and the new guest (if any), for both, a type + * name (at least if the guest is not null), and optionally an object name + * (but which may be omitted if the caller did not assign one). + */ +void check_unique_register( + void const *old_guest, std::string_view old_class, std::string_view old_name, + void const *new_guest, std::string_view new_class, + std::string_view new_name); + + +/// Like @ref check_unique_register, but for un-registering a guest. +/** Pass the guest which was registered, as well as the guest which is being + * unregistered, so that the function can check that they are the same one. + */ +void check_unique_unregister( + void const *old_guest, std::string_view old_class, std::string_view old_name, + void const *new_guest, std::string_view new_class, + std::string_view new_name); + + +/// Compute buffer size needed to escape binary data for use as a BYTEA. +/** This uses the hex-escaping format. The return value includes room for the + * "\x" prefix. + */ +inline constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept +{ + return 2 + (2 * binary_bytes) + 1; +} + + +/// Compute binary size from the size of its escaped version. +/** Do not include a terminating zero in `escaped_bytes`. + */ +inline constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept +{ + return (escaped_bytes - 2) / 2; +} + + +// TODO: Use actual binary type for "data". +/// Hex-escape binary data into a buffer. +/** The buffer must be able to accommodate + * `size_esc_bin(std::size(binary_data))` bytes, and the function will write + * exactly that number of bytes into the buffer. This includes a trailing + * zero. + */ +void PQXX_LIBEXPORT +esc_bin(std::basic_string_view binary_data, char buffer[]) noexcept; + + +/// Hex-escape binary data into a std::string. +std::string PQXX_LIBEXPORT +esc_bin(std::basic_string_view binary_data); + + +/// Reconstitute binary data from its escaped version. +void PQXX_LIBEXPORT +unesc_bin(std::string_view escaped_data, std::byte buffer[]); + + +/// Reconstitute binary data from its escaped version. +std::basic_string + PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data); + + +/// Transitional: std::ssize(), or custom implementation if not available. +template auto ssize(T const &c) +{ +#if defined(__cpp_lib_ssize) && __cplusplus >= __cpp_lib_ssize + return std::ssize(c); +#else + using signed_t = std::make_signed_t; + return static_cast(std::size(c)); +#endif // __cpp_lib_ssize +} + + +/// Helper for determining a function's parameter types. +/** This function has no definition. It's not meant to be actually called. + * It's just there for pattern-matching in the compiler, so we can use its + * hypothetical return value. + */ +template +std::tuple args_f(RETURN (&func)(ARGS...)); + + +/// Helper for determining a `std::function`'s parameter types. +/** This function has no definition. It's not meant to be actually called. + * It's just there for pattern-matching in the compiler, so we can use its + * hypothetical return value. + */ +template +std::tuple args_f(std::function const &); + + +/// Helper for determining a member function's parameter types. +/** This function has no definition. It's not meant to be actually called. + * It's just there for pattern-matching in the compiler, so we can use its + * hypothetical return value. + */ +template +std::tuple member_args_f(RETURN (CLASS::*)(ARGS...)); + + +/// Helper for determining a const member function's parameter types. +/** This function has no definition. It's not meant to be actually called. + * It's just there for pattern-matching in the compiler, so we can use its + * hypothetical return value. + */ +template +std::tuple member_args_f(RETURN (CLASS::*)(ARGS...) const); + + +/// Helper for determining a callable type's parameter types. +/** This specialisation should work for lambdas. + * + * This function has no definition. It's not meant to be actually called. + * It's just there for pattern-matching in the compiler, so we can use its + * hypothetical return value. + */ +template +auto args_f(CALLABLE const &f) + -> decltype(member_args_f(&CALLABLE::operator())); + + +/// A callable's parameter types, as a tuple. +template +using args_t = decltype(args_f(std::declval())); + + +/// Helper: Apply `strip_t` to each of a tuple type's component types. +/** This function has no definition. It is not meant to be called, only to be + * used to deduce the right types. + */ +template +std::tuple...> strip_types(std::tuple const &); + + +/// Take a tuple type and apply @ref strip_t to its component types. +template +using strip_types_t = decltype(strip_types(std::declval())); +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/version b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/version new file mode 100644 index 000000000..8dd5e48d4 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/version @@ -0,0 +1,7 @@ +/** libpqxx version info. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/version.hxx" +#include "pqxx/internal/header-post.hxx" + diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/version.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/version.hxx new file mode 100644 index 000000000..a159f1bed --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/version.hxx @@ -0,0 +1,55 @@ +/* Version info for libpqxx. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/version instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_VERSION + +# if !defined(PQXX_HEADER_PRE) +# error "Include libpqxx headers as , not ." +# endif + +/// Full libpqxx version string. +# define PQXX_VERSION "7.7.3" +/// Library ABI version. +# define PQXX_ABI "7.7" + +/// Major version number. +# define PQXX_VERSION_MAJOR 7 +/// Minor version number. +# define PQXX_VERSION_MINOR 7 + +# define PQXX_VERSION_CHECK check_pqxx_version_7_7 + +namespace pqxx::internal +{ +/// Library version check stub. +/** Helps detect version mismatches between libpqxx headers and the libpqxx + * library binary. + * + * Sometimes users run into trouble linking their code against libpqxx because + * they build their own libpqxx, but the system also has a different version + * installed. The declarations in the headers against which they compile their + * code will differ from the ones used to build the libpqxx version they're + * using, leading to confusing link errors. The solution is to generate a link + * error when the libpqxx binary is not the same version as the libpqxx headers + * used to compile the code. + * + * This function's definition is in the libpqxx binary, so it's based on the + * version as found in the binary. The headers contain a call to the function, + * whose name contains the libpqxx version as found in the headers. (The + * library build process will use its own local headers even if another version + * of the headers is installed on the system.) + * + * If the libpqxx binary was compiled for a different version than the user's + * code, linking will fail with an error: `check_pqxx_version_*_*` will not + * exist for the given version number. + */ +PQXX_LIBEXPORT int PQXX_VERSION_CHECK() noexcept; +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/zview b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/zview new file mode 100644 index 000000000..66ea2a625 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/zview @@ -0,0 +1,6 @@ +/** Zero-terminated string view class. + */ +// Actual definitions in .hxx file so editors and such recognize file type. +#include "pqxx/internal/header-pre.hxx" +#include "pqxx/zview.hxx" +#include "pqxx/internal/header-post.hxx" diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/zview.hxx b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/zview.hxx new file mode 100644 index 000000000..36a779f51 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/include/pqxx/zview.hxx @@ -0,0 +1,163 @@ +/* Zero-terminated string view. + * + * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/zview instead. + * + * Copyright (c) 2000-2022, Jeroen T. Vermeulen. + * + * See COPYING for copyright license. If you did not receive a file called + * COPYING with this source code, please notify the distributor of this + * mistake, or contact the author. + */ +#ifndef PQXX_H_ZVIEW +#define PQXX_H_ZVIEW + +#include +#include +#include + +#include "pqxx/types.hxx" + + +namespace pqxx +{ +/// Marker-type wrapper: zero-terminated `std::string_view`. +/** @warning Use this only if the underlying string is zero-terminated. + * + * When you construct a zview, you are promising that if the data pointer is + * non-null, the underlying string is zero-terminated. It otherwise behaves + * exactly like a std::string_view. + * + * The terminating zero is not "in" the string, so it does not count as part of + * the view's length. + * + * The added guarantee lets the view be used as a C-style string, which often + * matters since libpqxx builds on top of a C library. For this reason, zview + * also adds a @ref c_str method. + */ +class zview : public std::string_view +{ +public: + constexpr zview() noexcept = default; + + /// Convenience overload: construct using pointer and signed length. + constexpr zview(char const text[], std::ptrdiff_t len) : + std::string_view{text, static_cast(len)} + {} + + /// Convenience overload: construct using pointer and signed length. + constexpr zview(char text[], std::ptrdiff_t len) : + std::string_view{text, static_cast(len)} + {} + + /// Explicitly promote a `string_view` to a `zview`. + explicit constexpr zview(std::string_view other) noexcept : + std::string_view{other} + {} + + /// Construct from any initialiser you might use for `std::string_view`. + /** @warning Only do this if you are sure that the string is zero-terminated. + */ + template + explicit constexpr zview(Args &&...args) : + std::string_view(std::forward(args)...) + {} + + // C++20: constexpr. + /// @warning There's an implicit conversion from `std::string`. + zview(std::string const &str) noexcept : + std::string_view{str.c_str(), str.size()} + {} + + /// Construct a `zview` from a C-style string. + /** @warning This scans the string to discover its length. So if you need to + * do it many times, it's probably better to create the `zview` once and + * re-use it. + */ + constexpr zview(char const str[]) : std::string_view{str} {} + + /// Construct a `zview` from a string literal. + /** A C++ string literal ("foo") normally looks a lot like a pointer to + * char const, but that's not really true. It's actually an array of char, + * which _devolves_ to a pointer when you pass it. + * + * For the purpose of creating a `zview` there is one big difference: if we + * know the array's size, we don't need to scan through the string in order + * to find out its length. + */ + template + constexpr zview(char const (&literal)[size]) : zview(literal, size - 1) + {} + + /// Either a null pointer, or a zero-terminated text buffer. + [[nodiscard]] constexpr char const *c_str() const &noexcept + { + return data(); + } +}; + + +/// Support @ref zview literals. +/** You can "import" this selectively into your namespace, without pulling in + * all of the @ref pqxx namespace: + * + * ```cxx + * using pqxx::operator"" _zv; + * ``` + */ +constexpr zview operator"" _zv(char const str[], std::size_t len) noexcept +{ + return zview{str, len}; +} +} // namespace pqxx + + +#if defined(PQXX_HAVE_CONCEPTS) +/// A zview is a view. +template<> inline constexpr bool std::ranges::enable_view{true}; + + +/// A zview is a borrowed range. +template<> +inline constexpr bool std::ranges::enable_borrowed_range{true}; + +namespace pqxx::internal +{ +/// Concept: T is a known zero-terminated string type. +/** There's no unified API for these string types. It's just a check for some + * known types. Any code that makes use of the concept will still have to + * support each of these individually. + */ +template +concept ZString = std::is_convertible_v < strip_t, +char const * > or std::is_convertible_v, zview> or + std::is_convertible_v; +} // namespace pqxx::internal +#endif // PQXX_HAVE_CONCEPTS + + +namespace pqxx::internal +{ +/// Get a raw C string pointer. +inline constexpr char const *as_c_string(char const str[]) noexcept +{ + return str; +} +/// Get a raw C string pointer. +template +inline constexpr char const *as_c_string(char (&str)[N]) noexcept +{ + return str; +} +/// Get a raw C string pointer. +inline constexpr char const *as_c_string(pqxx::zview str) noexcept +{ + return str.c_str(); +} +// C++20: Make this constexpr. +/// Get a raw C string pointer. +inline char const *as_c_string(std::string const &str) noexcept +{ + return str.c_str(); +} +} // namespace pqxx::internal +#endif diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-config-version.cmake b/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-config-version.cmake new file mode 100644 index 000000000..c47d6956d --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-config-version.cmake @@ -0,0 +1,70 @@ +# This is a basic version file for the Config-mode of find_package(). +# It is used by write_basic_package_version_file() as input file for configure_file() +# to create a version-file which can be installed along a config.cmake file. +# +# The created file sets PACKAGE_VERSION_EXACT if the current version string and +# the requested version string are exactly the same and it sets +# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version, +# but only if the requested major version is the same as the current one. +# The variable CVF_VERSION must be set before calling configure_file(). + + +set(PACKAGE_VERSION "7.7.3") + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + + if("7.7.3" MATCHES "^([0-9]+)\\.") + set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}") + if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}") + endif() + else() + set(CVF_VERSION_MAJOR "7.7.3") + endif() + + if(PACKAGE_FIND_VERSION_RANGE) + # both endpoints of the range must have the expected major version + math (EXPR CVF_VERSION_MAJOR_NEXT "${CVF_VERSION_MAJOR} + 1") + if (NOT PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX_MAJOR STREQUAL CVF_VERSION_MAJOR) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX VERSION_LESS_EQUAL CVF_VERSION_MAJOR_NEXT))) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + elseif(PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + AND ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS_EQUAL PACKAGE_FIND_VERSION_MAX) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MAX))) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + else() + if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() + endif() +endif() + + +# if the installed project requested no architecture check, don't perform the check +if("FALSE") + return() +endif() + +# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it: +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "") + return() +endif() + +# check that the installed version has the same 32/64bit-ness as the one which is currently searching: +if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") + math(EXPR installedBits "8 * 8") + set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)") + set(PACKAGE_VERSION_UNSUITABLE TRUE) +endif() diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-config.cmake b/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-config.cmake new file mode 100644 index 000000000..cb25a05f2 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-config.cmake @@ -0,0 +1,4 @@ +include(CMakeFindDependencyMacro) +find_dependency(PostgreSQL) + +include("${CMAKE_CURRENT_LIST_DIR}/libpqxx-targets.cmake") diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-targets-noconfig.cmake b/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-targets-noconfig.cmake new file mode 100644 index 000000000..980f46098 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-targets-noconfig.cmake @@ -0,0 +1,19 @@ +#---------------------------------------------------------------- +# Generated CMake target import file. +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Import target "libpqxx::pqxx" for configuration "" +set_property(TARGET libpqxx::pqxx APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG) +set_target_properties(libpqxx::pqxx PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_NOCONFIG "CXX" + IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/lib/libpqxx-7.7.a" + ) + +list(APPEND _IMPORT_CHECK_TARGETS libpqxx::pqxx ) +list(APPEND _IMPORT_CHECK_FILES_FOR_libpqxx::pqxx "${_IMPORT_PREFIX}/lib/libpqxx-7.7.a" ) + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-targets.cmake b/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-targets.cmake new file mode 100644 index 000000000..4716fb7b2 --- /dev/null +++ b/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/cmake/libpqxx/libpqxx-targets.cmake @@ -0,0 +1,99 @@ +# Generated by CMake + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.6) + message(FATAL_ERROR "CMake >= 2.6.0 required") +endif() +cmake_policy(PUSH) +cmake_policy(VERSION 2.6...3.20) +#---------------------------------------------------------------- +# Generated CMake target import file. +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Protect against multiple inclusion, which would fail when already imported targets are added once more. +set(_targetsDefined) +set(_targetsNotDefined) +set(_expectedTargets) +foreach(_expectedTarget libpqxx::pqxx) + list(APPEND _expectedTargets ${_expectedTarget}) + if(NOT TARGET ${_expectedTarget}) + list(APPEND _targetsNotDefined ${_expectedTarget}) + endif() + if(TARGET ${_expectedTarget}) + list(APPEND _targetsDefined ${_expectedTarget}) + endif() +endforeach() +if("${_targetsDefined}" STREQUAL "${_expectedTargets}") + unset(_targetsDefined) + unset(_targetsNotDefined) + unset(_expectedTargets) + set(CMAKE_IMPORT_FILE_VERSION) + cmake_policy(POP) + return() +endif() +if(NOT "${_targetsDefined}" STREQUAL "") + message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_targetsDefined}\nTargets not yet defined: ${_targetsNotDefined}\n") +endif() +unset(_targetsDefined) +unset(_targetsNotDefined) +unset(_expectedTargets) + + +# Compute the installation prefix relative to this file. +get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +if(_IMPORT_PREFIX STREQUAL "/") + set(_IMPORT_PREFIX "") +endif() + +# Create imported target libpqxx::pqxx +add_library(libpqxx::pqxx STATIC IMPORTED) + +set_target_properties(libpqxx::pqxx PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include" + INTERFACE_LINK_LIBRARIES "/usr/lib/x86_64-linux-gnu/libpq.so" +) + +if(CMAKE_VERSION VERSION_LESS 2.8.12) + message(FATAL_ERROR "This file relies on consumers using CMake 2.8.12 or greater.") +endif() + +# Load information for each installed configuration. +get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +file(GLOB CONFIG_FILES "${_DIR}/libpqxx-targets-*.cmake") +foreach(f ${CONFIG_FILES}) + include(${f}) +endforeach() + +# Cleanup temporary variables. +set(_IMPORT_PREFIX) + +# Loop over all imported files and verify that they actually exist +foreach(target ${_IMPORT_CHECK_TARGETS} ) + foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} ) + if(NOT EXISTS "${file}" ) + message(FATAL_ERROR "The imported target \"${target}\" references the file + \"${file}\" +but this file does not exist. Possible reasons include: +* The file was deleted, renamed, or moved to another location. +* An install or uninstall procedure did not complete successfully. +* The installation package was faulty and contained + \"${CMAKE_CURRENT_LIST_FILE}\" +but not all the files it references. +") + endif() + endforeach() + unset(_IMPORT_CHECK_FILES_FOR_${target}) +endforeach() +unset(_IMPORT_CHECK_TARGETS) + +# This file does not depend on other imported targets which have +# been exported from the same project but in a separate export set. + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) +cmake_policy(POP) diff --git a/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/libpqxx-7.7.a b/ext/libpqxx-7.7.3/install/ubuntu22.04/lib/libpqxx-7.7.a new file mode 100644 index 0000000000000000000000000000000000000000..fb941debbbf9db796aaefff0ca29f2730cf7f917 GIT binary patch literal 4600578 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